home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Graphics Plus
/
Graphics Plus.iso
/
general
/
modelers
/
nff
/
haines.doc
< prev
next >
Wrap
Internet Message Format
|
1992-12-02
|
95KB
From markv@drizzle.UUCP Sun Sep 4 18:47:14 1988
Path: seismo!uunet!husc6!mailrus!cornell!uw-beaver!uoregon!markv
From: markv@uoregon.uoregon.edu (Mark VandeWettering)
Newsgroups: comp.graphics
Subject: Haine's NFF Package: Part 01/01
Message-ID: <2684@uoregon.uoregon.edu>
Date: 4 Sep 88 22:47:14 GMT
Reply-To: markv@drizzle.UUCP (Mark VandeWettering)
Organization: University of Oregon, Computer Science, Eugene OR
Lines: 2508
This is as it was posted to comp.graphics AGES ago. Have fun. If
someone figures out why "gears" turns out funny on my raytracer, let me
know, I am stumped.
# This is a shell archive. Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by eye!erich on November 17, 1987
# Contents: README makefile def.h lib.h lib.c
# balls.c gears.c mountain.c rings.c tetra.c tree.c
echo x - README
sed 's/^@//' > "README" <<'@//E*O*F README//'
STANDARD PROCEDURAL DATABASES, by Eric Haines, 3D/Eye, Inc.
[Created while under contract to Hewlett-Packard FSD and HP Laboratories]
Version 2.2, as of 11/17/87
address: 3D/Eye, Inc., 410 East Upland Road, Ithaca, NY 14850
e-mail: hpfcla!hpfcrs!eye!erich@hplabs.HP.COM
History
-------
This software package by Eric Haines is not copyrighted and can be used freely.
Versions 1.0 to 1.5 released February to June, 1987 for testing.
Version 2.0 released July, 1987.
Version 2.1 released August, 1987 - corrected info on speed of the HP 320,
other minor changes to README.
Version 2.2 released November, 1987 - shortened file names to <=12 characters,
procedure names to <= 32 characters, and ensured that all lines are <= 80
characters (including return character).
{This file uses tab characters worth 8 spaces}
Introduction
------------
This software is meant to act as a set of basic test images for ray tracing
and hidden surface algorithms. The programs generate databases of objects
which are fairly familiar and "standard" to the graphics community, such as a
fractal mountain, a tree, a recursively built tetrahedral structure, etc. I
created them originally for my own testing of a ray-tracer. My hope is that
these will be used by researchers to test algorithms. In this way, research on
algorithmic improvements can be compared on a more standard set of measures.
At present, one researcher ray-traces a car, another a tree, and the question
arises "How many cars to the tree?" With these databases we may be comparing
oranges and apples ("how many hypercubes to a timeshared VAX?"), but it sure
beats comparing oranges and orangutans.
Another use for these databases is to find how many vectors or polygons per
second a particular machine can actually generate. Talking with people
at SIGGRAPH '86, I found that various vendors would say "well, they claim
a rate of 5000 polygons/second, but their polygons are not our polygons".
With these tests, their polygons are our polygons. I admit a bias towards the
structure of the HP-SRX ("Renaissance") display system and its display software
"Starbase" in the way I structured the testing conditions (laid out later),
but this comes fairly close to preliminary PHIGS+ standards. For example,
these databases are not kind to machines using the painter's algorithm, but I
think this algorithm is inappropriate for good, general 3D modeling hardware.
These databases do not use polygons where an intensity or color is given for
each vertex, though such polygons are a feature of some machines (including
the SRX). They do include polygonal patches, where a normal is given for each
vertex, as this feature seems fairly common. Basically, if display hardware
cannot provide 8 light sources, use of normals at vertices, specular shading,
z-buffering, and perspective transformations, then I consider this machine
somewhat ineffective for use in a 3D modeling environment (which is mostly all
I care about--my personal bias). If you do not, please tell me why!
File Structure
--------------
Six different procedural database generators are included. These were
designed to span a fair range of primitives, modeling structures, lighting
and surface conditions, background conditions, and other factors. A complexity
factor is provided within each program to control the size of the database
generated.
This software package contains the following files:
README - what you are now reading
makefile - used to make the programs (in HP-UX Unix)
def.h - some useful "C" definitions
lib.h - globals and library routine declarations
lib.c - library of routines
balls.c - fractal ball object generator
gears.c - 3D array of interlocking gears
mountain.c - fractal mountain and 4 glass ball generator
rings.c - pyramid of dodecahedral rings generator
tetra.c - recursive tetrahedra generator
tree.c - tree generator
The compiled and linked programs will output a database in ASCII to stdout
containing viewing parameters, lighting conditions, material properties, and
geometric information. The data format is called the 'neutral file format'
(or NFF) and is outlined in lib.c. This format is meant to be minimal and
(hopefully) easy to attach to a user-written filter program which will convert
the output into a file format of your choosing.
Either of two sets of primitives can be selected for output. If
OUTPUT_FORMAT is defined as OUTPUT_CURVES, the primitives are spheres, cones,
cylinders, and polygons. If OUTPUT_FORMAT is set to OUTPUT_PATCHES, the
primitives are simply polygonal patches and polygons (i.e. all other primitives
are polygonalized). In this case OUTPUT_RESOLUTION is used to set the amount
of polygonalization of non-polygonal primitives. In general, OUTPUT_CURVES is
used for ray-trace timing tests, and OUTPUT_PATCHES for hidden surface and
other polygon-based algorithm timings.
SIZE_FACTOR is used to control the overall size of the database. Default
values have been chosen such that the maximum number of primitives less than
10,000 is output. One purpose of SIZE_FACTOR is to avoid limiting the uses of
these databases. Depending on the research being done and the computing
facilities available, a larger or smaller number of primitives may be desired.
SIZE_FACTOR can also be used to show how an algorithm's time changes as the
complexity increases.
Other parameters (for example, branching angles for "tree.c" and the
fractal dimension in "mountain.c") are included for your own enjoyment, and so
normally should not be changed if the database is used for timing tests.
Note that light intensities, ambient components, etc. are not specified.
These may be set however you prefer. The thrust of these databases is the
testing of rendering speeds, and so the actual color should not affect these
calculations. An ambient component should be used for all objects. A simple
formula for an intensity for each light and the ambient component is the
following: sqrt(# lights) / (# lights * 2).
Database Analysis
-----------------
The databases "mountain", and "tetra" consist of primitives of about the
same size in a localized cluster, "balls" and "tree" are more varied clusters,
and "rings" and "gears" are somewhat space-filling for the eye rays. Some
other facts about these databases:
balls gears mountain rings tetra tree
----- ----- -------- ----- ----- ----
primitives SP P PS YSP P OSP
(where S=sphere, P=polygon, Y=cylinder, O=cone, in # of objects order)
total prim. 7382 9345 8196 8401 4096 8191
polys 1417K 9345 8960 874K 4096 852K
rend. polys 707K 4448 5275 435K 2496 426K
ave. poly 0.26 173.6 53.2 2.95 35.4 0.09
vectors 4251K 55300 26880 2688K 12288 2621K
rend. vectors 4251K 54692 26074 2688K 12288 2621K
ave. vector 0.59 7.6 12.5 2.57 11.1 0.26
ave. # edges 3.00 5.92 3.00 3.08 3.00 3.08
lights 3 5 1 3 1 7
background 0% 7% 47% 0% 81% 35%
specular yes yes yes yes no no
transmitter no yes yes no no no
ave tree size 1.79 3.02 3.71 2.19 1.00 1.00
ave light rays 3.39 6.43 0.80 4.09 0.18 4.17
"total prim." is the total number of ray-tracing primitives (polygons,
spheres, cylinders and cones) in the scene. The number of polygons and vectors
generated is a function of the OUTPUT_RESOLUTION. The default value for this
parameter is 4 for all databases.
"polys" is the total number of polygons and polygonal patches generated
when using OUTPUT_PATCHES. "rend. polys" is the number of polygons actually
sent to the z-buffer (i.e. not culled and not fully clipped). "ave. poly" is
the average rendered polygon size in pixels for a 512 x 512 resolution. Note
that this size is the average of the number of pixels put on the screen by all
unculled, not fully clipped polygons. Culled polygons are not counted, nor are
pieces of polygons which are off-screen (clipped). For this statistic, all
transparent objects are considered opaque. The area was calculated directly
from the exact transformed vertices, not from the screen. Note that in
practice there usually are more pixels per polygon actually rendered, mostly
due to the polygon edges being fully rendered. It is because of this machine
dependency that the purely geometric area is given.
"vectors" is the total number of vectors generated. "rend. vector" is the
number of vectors which were not fully clipped off the screen (note that no
culling takes place in vector testing). "ave. vector" is similar to "ave.
poly", being the average length of all rendered vectors in pixels. Note that
culling is not performed for vectors. Again, the actual number of pixels
rendered is machine dependent and could be different than this average vector
length.
"lights" is simply the number of lights in a scene. "background" is the
percentage of background color (empty space) seen directly by the eye. Note
that any information in units of pixels is view dependent, and so is for the
view specified. "ave tree size" is the average number of rays in a ray tree.
"ave light rays" is the average number of shadow testing rays formed per tree
(note that if a surface is facing away from a light, or the background is hit,
a light ray is not formed). "K" means exactly 1000 (not 1024).
Testing Procedures
------------------
Below are listed the requirements for testing various algorithms. These test
conditions should be realizable by most systems, and are meant to represent a
common mode of operation for each algorithm. Special features which the
hardware supports (or standard features which it lacks) should be noted with
the statistics.
Hardware Vector Testing:
1) Two separate tests should be performed. One test should be done at
a resolution of 512 x 512. The second test should be done at the
maximum square resolution representable on the screen (e.g. the
HP SRX's resolution is 1280 x 1024, so run the tests at 1024 x 1024).
The first test is done so that the same sized polygons are used in
tests. The second test is done to test the display hardware using a
more commonly used image resolution.
2) At least 24 bit planes should be used for color rendering, if
available. If not, this should be noted and the best mapping mode
available should be used.
3) Vectors should be flat-shaded, with no hidden line removal performed
and no depth-cueing (but note these features if available).
4) The largest unit of display is the polygon. This means that no
queueing of edge lists or optimization of move/draw commands can be
performed by the user. All polygons must be rendered. This will mean
that many edges will be drawn twice (since they are shared by
polygons).
5) Timings will consist of only the time spent on the polygon draw calls,
including the overhead of calling the display routine itself. This
goal can most easily be realized by timing two versions of the testing
program. One version reads the database and displays the results
normally. The second version has all the system supplied vector
drawing commands removed, and so will output no vectors. We can
determine the time spent actually displaying vectors by taking the
difference of these two timings. This time divided by the total number
of vectors created by the database (listed in the table as "vectors")
is the vector rate for the database.
6) Two separate rates should be calculated from the average rates
computed. One is the display rate, which is the average of the gears,
mountain, and tetra databases; the other is the throughput rate, which
is the average of the balls, rings, and tree databases. The difference
is that the databases used to calculate the display rate are more
realistic as far as average polygon size presently used in hidden
surface comparisons. This rate should better reflect the day-to-day
performance expected of a machine. The databases for the throughput
rate are characterized by very small polygons, and so will reflect the
fastest speed of the hardware, as the effort to actually put the
vectors on the screen should be minimal.
Hardware Shaded Hidden Surface Testing:
1) As in hardware vector testing, two tests should be performed at the
two resolutions. A display and throughput rate should be calculated.
2) All polygons in the databases are guaranteed to be planar. Polygons
do not interpenetrate, and only in "rings.c" does cyclical overlapping
occur. The first three points of a polygon always form an angle less
than 180 degrees, and so can be used to compute the surface normal.
This computation should be done by the hardware if possible (and if
not, should be noted as such).
3) Polygons are one-sided for all databases (though transparent objects
may have to be treated as two-sided), and so the hardware can use the
surface normal to cull. It is not valid to cull (or perform any other
clipping or shading function) using the software.
4) Polygons should be displayed using hidden surface rendering methods.
No software sorting or other irregular manipulation of the data is
allowed: the polygons should be rendered on a first-come first-served
basis.
5) Up to seven light sources and an ambient contribution are used in the
databases. If the hardware does not have this many, note it and test
with the first few lights. Lights do not fall off with distance.
Shadowing is not performed (though it's an incredible feature if it
is).
6) Light sources are positional. If unavailable, assign the directional
lights a vector given by the light position and the viewing "lookat"
position.
7) Specular highlighting should be performed for surfaces with a specular
component. The simple Phong distribution model is sufficient (though
note improvements, if any).
8) Polygonal patches should have smooth shading, if available. In this
case a Gouraud interpolation of the colors calculated at the vertices
is the simplest algorithm allowed (again, note any more advanced
algorithms).
9) Transparency should be used if available, and the technique explained.
For example, the HP SRX uses "screen door" transparency, with a fill
pattern of a checkerboard. Transparent objects are then rendered by
filling every other pixel.
10) Overflow of color calculations is not guaranteed for these databases.
As such, this should be handled by scaling to the maximum color
contribution (e.g. r/g/b of 2 4 5 gets scaled to 0.4 0.8 1.0) if
available. If not, simple clamping should be used and noted (e.g. an
r/g/b of 0.8 1.1 1.5 is output as 0.8 1.0 1.0).
11) As in vector testing, timings should be done only for the actual
polygon rendering routines. This means such costs as changing fill
color, vertex format, or other state record information is not counted
in the timing performed, but rather a flat-out speed test of the
shaded polygon rate is made.
Other Hardware Tests:
A number of tests which could also be performed, but that I have ignored
for now, include the following:
1) Flat-shaded rendering: render polygons using only their fill colors.
Such renderings will look like mush, but would be useful for showing
the difference in speed for a flat-shade vs. shaded rendering.
2) Hidden-line rendering: render flat shaded polygons the same color as
the background, with edges a different color. Culling can be used to
eliminate polygons.
3) No polypatch rendering: render polygonal patches but ignore their
normal per vertex values (i.e. render them as simple flat polygons).
Again, useful to show the time cost of using normals/vertex.
Pointless, of course, for the databases without polygonal patches.
4) No specular rendering: render without specular highlighting. Useless
for the databases without specular highlighting.
5) Total cull and clip rendering: render with the lookat direction
reversed. Change the at vector to be equal to ( 2*from - at ) and up
to be -up. No objects will be displayed. This test gives a sense of
the speed of the cull and clip hardware.
Such a full battery of tests would yield some interesting results.
Hardware could be compared in a large number of categories, allowing users to
be able to select equipment optimized for their application. I feel the most
important rates are the vectors/second and the fully rendered polygons/second,
as these two display methods bracket the gamut of realism offered by standard
display hardware.
Ray-Tracer Testing:
1) Assume the same conditions apply as in the shaded hidden surface
testing outline, except as noted below. Note that the non-polygon
(OUTPUT_CURVES) format should be used for ray-tracing tests.
2) All opaque (non-transmitting) primitives can be considered one-sided
for rendering purposes, similar to how polygons were considered one-
-sided for hidden surface testing. Only the outside of sphere, cone,
and cylinder primitives are viewed in the databases.
3) Render at a resolution of 512 x 512 pixels, shooting rays at the
corners (meaning that at least 513 x 513 eye rays will be created).
The four corner contributions are averaged to arrive at a pixel
value. If this is not done, note this fact. No pixel subdivision
is performed.
4) The maximum tree depth is 5 (where the eye ray is of depth 1).
5) All rays hitting specular and transmitting objects spawn reflection
rays, unless the maximum ray depth was reached by the spawning ray.
No adaptive tree depth cutoff is allowed; that is, all rays must be
spawned (adaptive tree depth is a proven time saver and is also
dependent on the color model used).
6) All rays hitting transmitting objects spawn refraction rays, unless
the maximum ray depth was reached. Transmitting rays should be
refracted (i.e. should not pass straight through an object). If total
internal reflection of a ray occurs, then only a reflection ray is
generated at this node.
7) Assume no hierarchy is given with the database (for example, color
change cannot be used to signal a clustering).
8) Timing costs should be separated into at least two areas: preprocessing
and ray-tracing. Preprocessing includes all time spent initializing,
reading the database, and creating data structures needed to ray-trace.
Preprocessing should be all the constant cost operations--those that
do not change with the resolution of the image. Ray-tracing is the
time actually spent tracing the rays (i.e. everything that is not
preprocessing).
9) Other timing costs which would be of interest is a breakdown of
times spent in the preprocessing and for actual ray-tracing. Examples
include time spent creating a hierarchy, octree, or item buffer, and
times spent on intersection the various primitives and on calculating
the color.
One major complaint with simple timing tests is that they tell little about
the actual algorithm performance per se. One partial remedy to this problem is
to also include in statistics the number of object intersection tests performed
and the number of hits recorded. This information is useful for comparing
techniques. One example would be comparing various automatic hierarchy
algorithms by using these statistics.
Other information should be included in ray-trace timing results that is
often overlooked or ignored. Some basic information about the system
configuration should be made: average speed of machine in terms of mips,
Mflops, or Vaxen (i.e. the HP 320 is about 75 Kflops (single), which is about
30% of the speed of a VAX 11/780 FPA running vms 4.1, which is rated at 250
Kflops. However, some HP hardware engineer told me that the HP 320 is twice
the speed of an 11/780 - it is unclear if this was incorrect. He might be
right about overall machine speed in MIPS, for example); physical memory
size (i.e. 6 Mbytes); special hardware such as a floating point accelerator or
array processor, if different than the standard machine; system costs during
tests (i.e. single user mode vs. time-share with other heavy users vs. serious
operating system overhead); operating system (i.e. HP-UX version 5.2); and
language used (i.e. optimized "C", single point precision in general).
Timings
-------
Rendering time for test set on HP-320SRX:
Vector (seconds) | Z-buffer (seconds)
512 x 512 1024 x 1024 | 512 x 512 1024 x 1024
balls 369.03 380.32 | 284.52 320.93
gears 6.19 5.26 | 6.84 4.82
mountain 4.83 4.37 | 3.84 2.88
rings 232.28 243.65 | 181.28 202.11
tetra 2.24 2.08 | 1.85 1.40
tree 226.54 236.44 | 176.17 196.68
Calculated performance of test set on HP-320SRX:
Vector (vectors/second) | Z-buffer (polys/second)
512 x 512 1024 x 1024 | 512 x 512 1024 x 1024
gears 8937 10513 | 1365 1940
mountain 5563 6154 | 2331 3115
tetra 5496 5908 | 2216 2925
-------------------------------------------------------------------------------
Display average 6665 7525 | 1971 2660
(display average vector length for 512x512 is 10.4 pixels, 1024x1024 is 20.8)
(display average polygon size for 512x512 is 87.4 pixels, 1024x1024 is 349.6)
Vector (vectors/second) | Z-buffer (polys/second)
512 x 512 1024 x 1024 | 512 x 512 1024 x 1024
balls 11521 11179 | 4981 4416
rings 11572 11032 | 4819 4322
tree 11569 11084 | 4835 4331
-------------------------------------------------------------------------------
Throughput ave. 11554 11098 | 4878 4356
(throughput ave. vector length for 512x512 is 1.1 pixels, 1024x1024 is 2.3)
(throughput ave. polygon size for 512x512 is 1.1 pixels, 1024x1024 is 4.4)
The above times are computed from the average of 5 runs for each database.
The timing increment is 0.02 seconds (50 Hz). Variance over the runs was
quite small. Note that many of the tests at maximum square resolution
(1024 x 1024) for the hidden surface timings are faster than those for the low
resolution (512 x 512). The HP-320SRX is a workstation with 6 MByte memory and
floating point accelerator. 24 bit color was used for display, and the full
screen resolution is 1280 x 1024.
Rendering time of the ray-traced test set on HP-320SRX (512 x 512 pixels):
Setup Ray-Tracing | Polygon Sphere Cyl/Cone Bounding
(hr:min:sec) | Tests Tests Tests Vol. Tests
-------------------------------------------------------------------------------
balls 13:24 4:36:48 | 1084K 6253K 0 41219K
gears 13:49 10:32:51 | 12908K 0 0 92071K
mountain 11:46 2:59:58 | 3807K 4193K 0 31720K
rings 31:56 13:30:40 | 1203K 5393K 15817K 94388K
tetra 4:04 15:40 | 342K 0 0 1491K
tree 7:37 3:43:23 | 1199K 2218K 973K 20258K
A typical set of ray tracing intersection statistics for the tetra database is:
[these statistics should be the same for all users]
image size: 512 x 512
total number of pixels: 262144 [ 512 x 512 ]
total number of trees generated: 263169 [ 513 x 513 ]
total number of tree rays generated: 263169 [ no rays spawned ]
number of eye rays which hit background: 213219 [ 81% ]
average number of rays per tree: 1.000000
average number of rays per pixel: 1.003910
total number of shadow rays generated: 46262
[these tests vary depending on the ray-tracing algorithm used]
Ray/Item Tests Performed:
Polygonal: 342149 ( 55495 hit - 16.2 % )
Bounding Box: 1491181 ( 351754 hit - 23.6 % )
Total: 1833330 ( 407249 hit - 22.2 % )
Setup times are dominated by a verbose data format which causes a massive
(usually tenfold) increase in size from the NFF file format to our in-house
format. It should also be noted that for my sphere and bounding volume tests
there are more tests than are strictly needed. This is because I require each
light to be attached to an object (a sphere), which leads to extra testing in
both of these categories.
For what it's worth, my ray-tracer is based on hierarchical bounding boxes
generated using Goldsmith & Salmon's automatic hierarchy method (see IEEE CG&A
May 1987) and uses an item buffer (no light buffer yet) and shadow coherency.
Something to think about is how the octree, SEADS, and other such algorithms
perform when the background polygon dimensions are changed (thus changing the
size of the outermost enclosing box, which changes the octree encoding of the
environment). Same question with the effects of moving light source positions
along the line defined by the light and "lookat" positions.
Future Work
-----------
These databases are not meant to be the ultimate in standards, but are
presented as a first attempt at providing somewhat representative modelled
environments. A number of extensions to the file format should be provided
someday, along with new database generators which use them. The present
databases do not contain holed polygons, spline patches, polygonal mesh or
triangular strip data structures, or other proposed PHIGS+ extensions.
Modeling matrices are not output, and CSG combined primitives are not included.
As far as database geometry is concerned, most scenes have a preponderance
of small primitives, and in general very few objects ever get clipped. If you
find that these databases do not reflect the type of environments you render,
please write and explain why (or better yet, write one or more programs that
will generate your "typical" environments--maybe it will get put in the next
release).
Acknowledgements
----------------
I originally heard of this idea from Don Greenberg back in 1984. Some time
earlier he and Ed Catmull had talked over coming up with some standard
databases for testing algorithmic claims, and to them must go the credit for
the basic concept. Many thanks to the reviewers, listed alphabetically: Kells
Elmquist, Jeff Goldsmith, Donald Greenberg, Susan Spach, Rick Speer, John
Wallace, and Louise Watson. Other people who have freely offered their ideas
and opinions on this project include Brian Barsky, Andrew Glassner, Roy Hall,
Chip Hatfield, Tim Kay, John Recker, Paul Strauss, and Chan Verbeck. These
names are mentioned mostly as a listing of people interested in this idea.
They do not necessarily agree (and in some cases strongly disagree) with the
validity of the concept or the choice of databases.
Your comments and suggestions on these databases are appreciated. Please
send any timing results for software and hardware which you test.
@//E*O*F README//
chmod u=rw,g=r,o= README
echo x - makefile
sed 's/^@//' > "makefile" <<'@//E*O*F makefile//'
# makefile for standard procedural databases
# Version: 2.2 (11/17/87)
# Author: Eric Haines, 3D/Eye, Inc.
# Works on the HP 300 machines--probably needs to be modified to your system
# cc = C compiler
# -c compile only, do not try to link
CC=cc -c
INC=def.h lib.h
BASELIB=-lm -lmalloc
all: balls gears mountain rings tetra tree
lib.o: $(INC) lib.c
$(CC) lib.c
balls: lib.o balls.o
cc -o balls balls.o lib.o $(BASELIB)
balls.o: $(INC) balls.c
$(CC) balls.c
gears: lib.o gears.o
cc -o gears gears.o lib.o $(BASELIB)
gears.o: $(INC) gears.c
$(CC) gears.c
mountain: lib.o mountain.o
cc -o mountain mountain.o lib.o $(BASELIB)
mountain.o: $(INC) mountain.c
$(CC) mountain.c
rings: lib.o rings.o
cc -o rings rings.o lib.o $(BASELIB)
rings.o: $(INC) rings.c
$(CC) rings.c
tetra: lib.o tetra.o
cc -o tetra tetra.o lib.o $(BASELIB)
tetra.o: $(INC) tetra.c
$(CC) tetra.c
tree: lib.o tree.o
cc -o tree tree.o lib.o $(BASELIB)
tree.o: $(INC) tree.c
$(CC) tree.c
@//E*O*F makefile//
chmod u=rw,g=r,o= makefile
echo x - def.h
sed 's/^@//' > "def.h" <<'@//E*O*F def.h//'
/*
* def.h contains some useful definitions for "C" programs.
*
* Version: 2.2 (11/17/87)
* Author: Eric Haines, 3D/Eye, Inc.
*/
#define EPSILON 5.0e-6
#ifndef FALSE
#define FALSE 0
#endif
#ifndef NULL
#define NULL 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
#ifndef PI
#define PI 3.141592653589793
#endif
typedef double MATRIX[4][4] ; /* row major form */
typedef struct {
double x ;
double y ;
double z ;
double w ;
} COORD4, *COORD4_P ;
#define ABSOLUTE(A) ( (A) < 0 ? -(A) : (A) )
#define FRACTION(A) ( (A) - (long)(A) )
#define MAX(A,B) ( (A) > (B) ? (A) : (B) )
#define MAX3(A,B,C) ( MAX( MAX( A,B ), C ) )
#define MIN(A,B) ( (A) < (B) ? (A) : (B) )
#define MIN3(A,B,C) ( MIN( MIN( A,B ), C ) )
#define SQR(A) ( (A) * (A) )
#define ADD2_COORD(r,a) { (r).x += (a).x; (r).y += (a).y;\
(r).z += (a).z; }
#define ADD3_COORD(r,a,b) { (r).x = (a).x + (b).x;\
(r).y = (a).y + (b).y;\
(r).z = (a).z + (b).z; }
#define COPY_COORD(r,a) { (r).x = (a).x; (r).y = (a).y; (r).z = (a).z;}
#define COPY_COORD4(r,a) { (r).x = (a).x; (r).y = (a).y; (r).z = (a).z;\
(r).w = (a).w; }
#define CROSS(r,a,b) { (r).x = (a).y * (b).z - (a).z * (b).y;\
(r).y = (a).z * (b).x - (a).x * (b).z;\
(r).z = (a).x * (b).y - (a).y * (b).x; }
#define DOT_PRODUCT(a,b) ( (a).x * (b).x +\
(a).y * (b).y +\
(a).z * (b).z )
#define SET_COORD(r,a,b,c) { (r).x = (a); (r).y = (b); (r).z = (c); }
#define SET_COORD4(r,a,b,c,d) { (r).x = (a); (r).y = (b); (r).z = (c);\
(r).w = (d); }
#define SUB2_COORD(r,a) { (r).x -= (a).x; (r).y -= (a).y;\
(r).z -= (a).z; }
#define SUB3_COORD(r,a,b) { (r).x = (a).x - (b).x;\
(r).y = (a).y - (b).y;\
(r).z = (a).z - (b).z; }
@//E*O*F def.h//
chmod u=rw,g=r,o= def.h
echo x - lib.h
sed 's/^@//' > "lib.h" <<'@//E*O*F lib.h//'
/*
* lib.h - vector library definitions
*
* Version: 2.2 (11/17/87)
* Author: Eric Haines, 3D/Eye, Inc.
*/
#define X_AXIS 0
#define Y_AXIS 1
#define Z_AXIS 2
/* Output library definitions */
#define OUTPUT_CURVES 0 /* true curve output */
#define OUTPUT_PATCHES 1 /* polygonal patches output */
#define OUTPUT_RESOLUTION 4 /* amount of polygonalization */
double lib_normalize_coord3() ;
double lib_gauss_rand() ;
@//E*O*F lib.h//
chmod u=rw,g=r,o= lib.h
echo x - lib.c
sed 's/^@//' > "lib.c" <<'@//E*O*F lib.c//'
/*
* lib.c - a library of vector operations, a random number generator, and
* object output routines.
*
* Version: 2.2 (11/17/87)
* Author: Eric Haines, 3D/Eye, Inc.
*/
#include <stdio.h>
#include <math.h>
#include <memory.h>
#include "def.h"
#include "lib.h"
/*
* Normalize the vector (X,Y,Z) so that X*X + Y*Y + Z*Z = 1.
*
* The normalization divisor is returned. If the divisor is zero, no
* normalization occurs.
*
*/
double lib_normalize_coord3( cvec )
COORD4 *cvec;
{
double divisor;
divisor = sqrt( (double)DOT_PRODUCT( (*cvec), (*cvec) ) ) ;
if ( divisor != 0.0 ) {
cvec->x /= divisor;
cvec->y /= divisor;
cvec->z /= divisor;
}
return( divisor );
}
/*
* Set all matrix elements to zero.
*/
lib_zero_matrix( mx )
MATRIX mx ;
{
long i, j ;
for ( i = 0 ; i < 4 ; ++i ) {
for ( j = 0 ; j < 4 ; ++j ) {
mx[i][j] = 0.0 ;
}
}
}
/*
* Create identity matrix.
*/
lib_create_identity_matrix( mx )
MATRIX mx ;
{
long i ;
lib_zero_matrix( mx ) ;
for ( i = 0 ; i < 4 ; ++i ) {
mx[i][i] = 1.0 ;
}
}
/*
* Create a rotation matrix along the given axis by the given angle in radians.
*/
lib_create_rotate_matrix( mx, axis, angle )
MATRIX mx ;
long axis ;
double angle ;
{
double cosine ;
double sine ;
lib_zero_matrix( mx ) ;
cosine = cos( (double)angle ) ;
sine = sin( (double)angle ) ;
switch ( axis ) {
case X_AXIS:
mx[0][0] = 1.0 ;
mx[1][1] = mx[2][2] = cosine ;
mx[1][2] = sine ;
mx[2][1] = -sine ;
break ;
case Y_AXIS:
mx[1][1] = 1.0 ;
mx[0][0] = mx[2][2] = cosine ;
mx[2][0] = sine ;
mx[0][2] = -sine ;
break ;
case Z_AXIS:
mx[2][2] = 1.0 ;
mx[0][0] = mx[1][1] = cosine ;
mx[0][1] = sine ;
mx[1][0] = -sine ;
break ;
}
mx[3][3] = 1.0 ;
}
/*
* Create a rotation matrix along the given axis by the given angle in radians.
* The axis is a set of direction cosines.
*/
lib_create_axis_rotate_matrix( mx, rvec, angle )
MATRIX mx ;
COORD4 *rvec ;
double angle ;
{
COORD4 axis ;
double cosine ;
double one_minus_cosine ;
double sine ;
lib_zero_matrix( mx ) ;
COPY_COORD( axis, (*rvec) ) ;
cosine = cos( (double)angle ) ;
sine = sin( (double)angle ) ;
one_minus_cosine = 1.0 - cosine ;
mx[0][0] = SQR(axis.x) + (1.0 - SQR(axis.x)) * cosine ;
mx[0][1] = axis.x * axis.y * one_minus_cosine + axis.z * sine ;
mx[0][2] = axis.x * axis.z * one_minus_cosine - axis.y * sine ;
mx[1][0] = axis.x * axis.y * one_minus_cosine - axis.z * sine ;
mx[1][1] = SQR(axis.y) + (1.0 - SQR(axis.y)) * cosine ;
mx[1][2] = axis.y * axis.z * one_minus_cosine + axis.x * sine ;
mx[2][0] = axis.x * axis.z * one_minus_cosine + axis.y * sine ;
mx[2][1] = axis.y * axis.z * one_minus_cosine - axis.x * sine ;
mx[2][2] = SQR(axis.z) + (1.0 - SQR(axis.z)) * cosine ;
mx[3][3] = 1.0 ;
}
/*
* Multiply a 4 element vector by a matrix.
*/
lib_transform_coord( vres, vec, mx )
COORD4 *vres ;
COORD4 *vec ;
MATRIX mx ;
{
vres->x =
vec->x*mx[0][0] + vec->y*mx[1][0] + vec->z*mx[2][0] + vec->w*mx[3][0] ;
vres->y =
vec->x*mx[0][1] + vec->y*mx[1][1] + vec->z*mx[2][1] + vec->w*mx[3][1] ;
vres->z =
vec->x*mx[0][2] + vec->y*mx[1][2] + vec->z*mx[2][2] + vec->w*mx[3][2] ;
vres->w =
vec->x*mx[0][3] + vec->y*mx[1][3] + vec->z*mx[2][3] + vec->w*mx[3][3] ;
}
/*
* Multiply two 4x4 matrices.
*/
lib_matrix_multiply( mxres, mx1, mx2 )
MATRIX mxres ;
MATRIX mx1 ;
MATRIX mx2 ;
{
long i ;
long j ;
for ( i = 0; i < 4; i++ ) {
for ( j = 0; j < 4; j++ ) {
mxres[i][j] = mx1[i][0]*mx2[0][j] +
mx1[i][1]*mx2[1][j] +
mx1[i][2]*mx2[2][j] +
mx1[i][3]*mx2[3][j] ;
}
}
}
/*
* Rotate a vector pointing towards the major-axis faces (i.e. the major-axis
* component of the vector is defined as the largest value) 90 degrees to
* another cube face. Mod_face is a face number.
*
* If the routine is called six times, with mod_face=0..5, the vector will be
* rotated to each face of a cube. Rotations are:
* mod_face = 0 mod 3, +Z axis rotate
* mod_face = 1 mod 3, +X axis rotate
* mod_face = 2 mod 3, -Y axis rotate
*/
lib_rotate_cube_face( vec, major_axis, mod_face )
COORD4 *vec ;
long major_axis ;
long mod_face ;
{
double swap ;
mod_face = (mod_face+major_axis) % 3 ;
if ( mod_face == 0 ) {
swap = vec->x ;
vec->x = -vec->y ;
vec->y = swap ;
}
else if ( mod_face == 1 ) {
swap = vec->y ;
vec->y = -vec->z ;
vec->z = swap ;
}
else {
swap = vec->x ;
vec->x = -vec->z ;
vec->z = swap ;
}
}
/*
* Portable gaussian random number generator (from "Numerical Recipes", GASDEV)
* Returns a uniform random deviate between 0.0 and 1.0. 'iseed' must be
* less than M1 to avoid repetition, and less than (2**31-C1)/A1 [= 300718]
* to avoid overflow.
*/
#define M1 134456
#define IA1 8121
#define IC1 28411
#define RM1 1.0/M1
double lib_gauss_rand(iseed)
long iseed ;
{
double fac ;
long ix1, ix2 ;
double r ;
double v1, v2 ;
ix2 = iseed ;
do {
ix1 = (IC1+ix2*IA1) % M1 ;
ix2 = (IC1+ix1*IA1) % M1 ;
v1 = ix1 * 2.0 * RM1 - 1.0 ;
v2 = ix2 * 2.0 * RM1 - 1.0 ;
r = v1*v1 + v2*v2 ;
} while ( r >= 1.0 ) ;
fac = sqrt( (double)( -2.0 * log( (double)r ) / r ) ) ;
return( v1 * fac ) ;
}
/* OUTPUT ROUTINES
*
* Files are output as lines of text. For each entity, the first line
* defines its type. The rest of the first line and possibly other lines
* contain further information about the entity. Entities include:
*
* "v" - viewing vectors and angles
* "l" - positional light location
* "b" - background color
* "f" - object material properties
* "c" - cone or cylinder primitive
* "s" - sphere primitive
* "p" - polygon primitive
* "pp" - polygonal patch primitive
*/
/*
* Output viewpoint location. The parameters are:
* From: the eye location.
* At: a position to be at the center of the image. A.k.a. "lookat"
* Up: a vector defining which direction is up.
*
* Note that no assumptions are made about normalizing the data (e.g. the
* from-at distance does not have to be 1). Also, vectors are not
* required to be perpendicular to each other.
*
* For all databases some viewing parameters are always the same:
*
* Viewing angle is defined as from the center of top pixel row to bottom
* pixel row and left column to right column.
* Yon is "at infinity."
* Resolution is always 512 x 512.
*/
lib_output_viewpoint( from, at, up, angle, hither, resx, resy )
COORD4 *from ;
COORD4 *at ;
COORD4 *up ;
double angle ;
double hither ;
long resx ;
long resy ;
{
printf( "v\n" ) ;
printf( "from %g %g %g\n", from->x, from->y, from->z ) ;
printf( "at %g %g %g\n", at->x, at->y, at->z ) ;
printf( "up %g %g %g\n", up->x, up->y, up->z ) ;
printf( "angle %g\n", angle ) ;
printf( "hither %g\n", hither ) ;
printf( "resolution %d %d\n", resx, resy ) ;
}
/*
* Output light. A light is defined by position. All lights have the same
* intensity.
*
*/
lib_output_light( center_pt )
COORD4 *center_pt ;
{
printf( "l %g %g %g\n", center_pt->x, center_pt->y, center_pt->z ) ;
}
/*
* Output background color. A color is simply RGB (monitor dependent, but
* that's life). The format is:
* "b" red green blue
*/
lib_output_background_color( color )
COORD4 *color ;
{
printf( "b %g %g %g\n", color->x, color->y, color->z ) ;
}
/*
* Output a color and shading parameters for the object in the format:
* "f" red green blue Kd Ks Shine T index_of_refraction
*
* Kd is the diffuse component, Ks the specular, Shine is the Phong cosine
* power for highlights, T is transmittance (fraction of light passed per
* unit). 0 <= Kd <= 1 and 0 <= Ks <= 1, though it is not required that
* Kd + Ks == 1.
*
* The fill color is used to color the objects following it until a new color
* is assigned or the file ends.
*/
lib_output_color( color, kd, ks, shine, t, i_of_r )
COORD4 *color ;
double kd ;
double ks ;
double shine ;
double t ;
double i_of_r ;
{
printf( "f %g %g %g %g %g %g %g %g\n", color->x, color->y, color->z,
kd, ks, shine, t, i_of_r ) ;
}
/*
* Output cylinder or cone. A cylinder is defined as having a radius and an
* axis defined by two points, which also define the top and bottom edge of the
* cylinder. A cone is defined similarly, the difference being that the apex
* and base radii are different. The apex radius is defined as being smaller
* than the base radius. Note that the surface exists without endcaps.
*
* If format=OUTPUT_CURVES, output the cylinder/cone in format:
* "c"
* base.x base.y base.z base_radius
* apex.x apex.y apex.z apex_radius
*
* If the format=OUTPUT_POLYGONS, the surface is polygonalized and output.
* (4*OUTPUT_RESOLUTION) polygons are output as rectangles by
* lib_output_polypatch.
*/
lib_output_cylcone( base_pt, apex_pt, format )
COORD4 *base_pt ;
COORD4 *apex_pt ;
long format ;
{
double angle ;
COORD4 axis ;
COORD4 dir ;
double divisor ;
COORD4 lip_norm[4], lip_pt[4] ;
MATRIX mx ;
COORD4 norm_axis ;
long num_pol ;
COORD4 start_norm, start_radius[4] ;
if ( format == OUTPUT_CURVES ) {
printf( "c\n" ) ;
printf( "%g %g %g %g\n",
base_pt->x, base_pt->y, base_pt->z, base_pt->w ) ;
printf( "%g %g %g %g\n",
apex_pt->x, apex_pt->y, apex_pt->z, apex_pt->w ) ;
}
else {
SUB3_COORD( axis, (*apex_pt), (*base_pt) ) ;
COPY_COORD( norm_axis, axis ) ;
lib_normalize_coord3( &norm_axis ) ;
dir.x = 0.0 ; dir.y = 0.0 ; dir.z = 1.0 ; dir.w = 0.0 ;
CROSS( start_norm, axis, dir ) ;
divisor = lib_normalize_coord3( &start_norm ) ;
if ( ABSOLUTE( divisor ) < EPSILON ) {
dir.x = 1.0 ; dir.y = 0.0 ; dir.z = 0.0 ;
CROSS( start_norm, axis, dir ) ;
lib_normalize_coord3( &start_norm ) ;
}
start_radius[0].x = start_norm.x * base_pt->w ;
start_radius[0].y = start_norm.y * base_pt->w ;
start_radius[0].z = start_norm.z * base_pt->w ;
start_radius[0].w = 0.0 ;
ADD3_COORD( lip_pt[0], (*base_pt), start_radius[0] ) ;
start_radius[1].x = start_norm.x * apex_pt->w ;
start_radius[1].y = start_norm.y * apex_pt->w ;
start_radius[1].z = start_norm.z * apex_pt->w ;
start_radius[1].w = 0.0 ;
ADD3_COORD( lip_pt[1], (*apex_pt), start_radius[1] ) ;
COPY_COORD4( lip_norm[0], start_norm ) ;
COPY_COORD4( lip_norm[1], start_norm ) ;
for ( num_pol = 0 ; num_pol < 4*OUTPUT_RESOLUTION ; ++num_pol ) {
COPY_COORD4( lip_pt[3], lip_pt[0] ) ;
COPY_COORD4( lip_pt[2], lip_pt[1] ) ;
COPY_COORD4( lip_norm[3], lip_norm[0] ) ;
COPY_COORD4( lip_norm[2], lip_norm[1] ) ;
angle = 2.0 * PI *
(double)( num_pol+1 ) / (double)( 4*OUTPUT_RESOLUTION ) ;
lib_create_axis_rotate_matrix( mx, &norm_axis, angle ) ;
lib_transform_coord( &lip_pt[0], &start_radius[0], mx ) ;
ADD2_COORD( lip_pt[0], (*base_pt) ) ;
lib_transform_coord( &lip_pt[1], &start_radius[1], mx ) ;
ADD2_COORD( lip_pt[1], (*apex_pt) ) ;
lib_transform_coord( &lip_norm[0], &start_norm, mx ) ;
COPY_COORD4( lip_norm[1], lip_norm[0] ) ;
lib_output_polypatch( 4, lip_pt, lip_norm ) ;
}
}
}
/*
* Output sphere. A sphere is defined by a radius and center position.
*
* If format=OUTPUT_CURVES, output the sphere in format:
* "s" center.x center.y center.z radius
*
* If the format=OUTPUT_POLYGONS, the sphere is polygonalized and output.
* The sphere is polygonalized by splitting it into 6 faces (of a cube
* projected onto the sphere) and dividing these faces by equally spaced
* great circles. OUTPUT_RESOLUTION affects the number of great circles.
* (6*2*OUTPUT_RESOLUTION*OUTPUT_RESOLUTION) polygons are output as triangles
* using lib_output_polypatch.
*/
lib_output_sphere( center_pt, format )
COORD4 *center_pt ;
long format ;
{
double angle ;
COORD4 edge_norm[3], edge_pt[3] ;
long num_face, num_edge, num_tri, num_vert ;
COORD4 x_axis[OUTPUT_RESOLUTION+1], y_axis[OUTPUT_RESOLUTION+1] ;
COORD4 pt[OUTPUT_RESOLUTION+1][OUTPUT_RESOLUTION+1] ;
COORD4 mid_axis ;
MATRIX rot_mx ;
long u_pol, v_pol ;
if ( format == OUTPUT_CURVES ) {
printf( "s %g %g %g %g\n",
center_pt->x, center_pt->y, center_pt->z, center_pt->w ) ;
}
else {
/* calculate axes used to find grid points */
for ( num_edge = 0 ; num_edge <= OUTPUT_RESOLUTION ; ++num_edge ) {
angle = (PI/4.0) * (2.0*(double)num_edge/OUTPUT_RESOLUTION - 1.0) ;
mid_axis.w = 0.0 ;
mid_axis.x = 1.0 ; mid_axis.y = 0.0 ; mid_axis.z = 0.0 ;
lib_create_rotate_matrix( rot_mx, Y_AXIS, angle ) ;
lib_transform_coord( &x_axis[num_edge], &mid_axis, rot_mx ) ;
mid_axis.x = 0.0 ; mid_axis.y = 1.0 ; mid_axis.z = 0.0 ;
lib_create_rotate_matrix( rot_mx, X_AXIS, angle ) ;
lib_transform_coord( &y_axis[num_edge], &mid_axis, rot_mx ) ;
}
/* set up grid of points on +Z sphere surface */
for ( u_pol = 0 ; u_pol <= OUTPUT_RESOLUTION ; ++u_pol ) {
for ( v_pol = 0 ; v_pol <= OUTPUT_RESOLUTION ; ++v_pol ) {
CROSS( pt[u_pol][v_pol], x_axis[u_pol], y_axis[v_pol] ) ;
lib_normalize_coord3( &pt[u_pol][v_pol] ) ;
pt[u_pol][v_pol].w = 1.0 ;
}
}
for ( num_face = 0 ; num_face < 6 ; ++num_face ) {
/* transform points to cube face */
for ( u_pol = 0 ; u_pol <= OUTPUT_RESOLUTION ; ++u_pol ) {
for ( v_pol = 0 ; v_pol <= OUTPUT_RESOLUTION ; ++v_pol ) {
lib_rotate_cube_face( &pt[u_pol][v_pol]
, Z_AXIS
, num_face
) ;
}
}
/* output grid */
for ( u_pol = 0 ; u_pol < OUTPUT_RESOLUTION ; ++u_pol ) {
for ( v_pol = 0 ; v_pol < OUTPUT_RESOLUTION ; ++v_pol ) {
for ( num_tri = 0 ; num_tri < 2 ; ++num_tri ) {
for ( num_edge = 0 ; num_edge < 3 ; ++num_edge ) {
num_vert = (num_tri*2 + num_edge) % 4 ;
if ( num_vert == 0 ) {
COPY_COORD4( edge_pt[num_edge],
pt[u_pol][v_pol] ) ;
}
else if ( num_vert == 1 ) {
COPY_COORD4( edge_pt[num_edge],
pt[u_pol][v_pol+1] ) ;
}
else if ( num_vert == 2 ) {
COPY_COORD4( edge_pt[num_edge],
pt[u_pol+1][v_pol+1] ) ;
}
else {
COPY_COORD4( edge_pt[num_edge],
pt[u_pol+1][v_pol] ) ;
}
COPY_COORD4( edge_norm[num_edge],
edge_pt[num_edge] ) ;
edge_pt[num_edge].x =
edge_pt[num_edge].x * center_pt->w +
center_pt->x ;
edge_pt[num_edge].y =
edge_pt[num_edge].y * center_pt->w +
center_pt->y ;
edge_pt[num_edge].z =
edge_pt[num_edge].z * center_pt->w +
center_pt->z ;
}
lib_output_polypatch( 3, edge_pt, edge_norm ) ;
}
}
}
}
}
}
/*
* Output polygon. A polygon is defined by a set of vertices. With these
* databases, a polygon is defined to have all points coplanar. A polygon has
* only one side, with the order of the vertices being counterclockwise as you
* face the polygon (right-handed coordinate system).
*
* The output format is always:
* "p" total_vertices
* vert1.x vert1.y vert1.z
* [etc. for total_vertices polygons]
*
*/
lib_output_polygon( tot_vert, vert )
long tot_vert ;
COORD4 *vert ;
{
long num_vert ;
printf( "p %d\n", tot_vert ) ;
for ( num_vert = 0 ; num_vert < tot_vert ; ++num_vert ) {
printf( "%g %g %g\n", vert[num_vert].x
, vert[num_vert].y
, vert[num_vert].z
) ;
}
}
/*
* Output polygonal patch. A patch is defined by a set of vertices and their
* normals. With these databases, a patch is defined to have all points
* coplanar. A patch has only one side, with the order of the vertices being
* counterclockwise as you face the patch (right-handed coordinate system).
*
* The output format is always:
* "pp" total_vertices
* vert1.x vert1.y vert1.z norm1.x norm1.y norm1.z
* [etc. for total_vertices polygonal patches]
*
*/
lib_output_polypatch( tot_vert, vert, norm )
long tot_vert ;
COORD4 *vert ;
COORD4 *norm ;
{
long num_vert ;
printf( "pp %d\n", tot_vert ) ;
for ( num_vert = 0 ; num_vert < tot_vert ; ++num_vert ) {
printf( "%g %g %g %g %g %g\n", vert[num_vert].x
, vert[num_vert].y
, vert[num_vert].z
, norm[num_vert].x
, norm[num_vert].y
, norm[num_vert].z
) ;
}
}
@//E*O*F lib.c//
chmod u=rw,g=r,o= lib.c
echo x - balls.c
sed 's/^@//' > "balls.c" <<'@//E*O*F balls.c//'
/*
* balls.c - Create a set of shiny spheres, with each sphere blooming sets of
* 9 more spheres with 1/3rd radius. None of the spheres are clipped. A
* square floor polygon is added. Three light sources.
*
* Version: 2.2 (11/17/87)
* Author: Eric Haines, 3D/Eye, Inc.
*
* SIZE_FACTOR determines the number of objects output.
* Total spheres = sum of n=0,SF of (9**SF).
*
* SIZE_FACTOR # spheres # squares
* 1 10 1
* 2 91 1
* 3 820 1
*
* 4 7381 1
*/
#include <stdio.h>
#include <math.h>
#include <memory.h>
#include "def.h"
#include "lib.h"
#define OUTPUT_FORMAT OUTPUT_CURVES
#define SIZE_FACTOR 4
static COORD4 objset[9] ;
main(argc,argv)
int argc ;
char *argv[] ;
{
COORD4 back_color, obj_color ;
COORD4 backg[4], bvec, light ;
COORD4 from, at, up ;
COORD4 center_pt, direction ;
double radius ;
/* set radius of sphere which would enclose entire object */
radius = 1.0 ;
/* output viewpoint */
SET_COORD( from, 2.1, 1.3, 1.7 ) ;
SET_COORD( at, 0.0, 0.0, 0.0 ) ;
SET_COORD( up, 0.0, 0.0, 1.0 ) ;
lib_output_viewpoint( &from, &at, &up, 45.0, 1.0, 512, 512 ) ;
/* output background color - UNC sky blue */
SET_COORD( back_color, 0.078, 0.361, 0.753 ) ;
lib_output_background_color( &back_color ) ;
/* output light sources */
SET_COORD( light, 4.0, 3.0, 2.0 ) ;
lib_output_light( &light ) ;
SET_COORD( light, 1.0, -4.0, 4.0 ) ;
lib_output_light( &light ) ;
SET_COORD( light, -3.0, 1.0, 5.0 ) ;
lib_output_light( &light ) ;
/* output floor polygon - beige */
SET_COORD( back_color, 1.0, 0.75, 0.33 ) ;
lib_output_color( &back_color, 1.0, 0.0, 0.0, 0.0, 0.0 ) ;
bvec.x = bvec.y = radius * 12.0 ;
bvec.z = -radius / 2.0 ;
SET_COORD( backg[0], bvec.x, bvec.y, bvec.z ) ;
SET_COORD( backg[1], -bvec.x, bvec.y, bvec.z ) ;
SET_COORD( backg[2], -bvec.x, -bvec.y, bvec.z ) ;
SET_COORD( backg[3], bvec.x, -bvec.y, bvec.z ) ;
lib_output_polygon( 4, backg ) ;
/* set up object color - off white */
SET_COORD( obj_color, 1.0, 0.9, 0.7 ) ;
lib_output_color( &obj_color, 0.5, 0.5, 3.0, 0.0, 0.0 ) ;
/* create set of spawned points */
create_objset() ;
/* compute and output object */
SET_COORD4( center_pt, 0.0, 0.0, 0.0, radius / 2.0 ) ;
SET_COORD4( direction, 0.0, 0.0, 1.0, 1.0/3.0 ) ;
output_object( SIZE_FACTOR, ¢er_pt, &direction ) ;
}
/* Create the set of 9 vectors needed to generate the sphere set. */
/* Uses global 'objset' */
create_objset()
{
COORD4 axis, temp_pt, trio_dir[3] ;
double dist ;
MATRIX mx ;
long num_set, num_vert ;
dist = 1.0 / sqrt( (double)2.0 ) ;
SET_COORD4( trio_dir[0], dist, dist, 0.0, 0.0 ) ;
SET_COORD4( trio_dir[1], dist, 0.0, -dist, 0.0 ) ;
SET_COORD4( trio_dir[2], 0.0, dist, -dist, 0.0 ) ;
SET_COORD( axis, 1.0, -1.0, 0.0 ) ;
lib_normalize_coord3( &axis ) ;
lib_create_axis_rotate_matrix(
mx,
&axis,
asin( (double) ( 2.0 / sqrt( (double)6.0 ) ) ) ) ;
for ( num_vert = 0 ; num_vert < 3 ; ++num_vert ) {
lib_transform_coord( &temp_pt, &trio_dir[num_vert], mx ) ;
COPY_COORD( trio_dir[num_vert], temp_pt ) ;
}
for ( num_set = 0 ; num_set < 3 ; ++num_set ) {
lib_create_rotate_matrix( mx, Z_AXIS, num_set*2.0*PI/3.0 ) ;
for ( num_vert = 0 ; num_vert < 3 ; ++num_vert ) {
lib_transform_coord( &objset[num_set*3+num_vert],
&trio_dir[num_vert], mx ) ;
}
}
}
/*
* Output the parent sphere, then output the children of the sphere.
* Uses global 'objset'.
*/
output_object( depth, center, direction )
long depth ;
COORD4 *center ;
COORD4 *direction ;
{
double angle ;
COORD4 axis, z_axis ;
COORD4 child_pt, child_dir ;
MATRIX mx ;
long num_vert ;
double scale ;
/* output sphere at location & radius defined by center */
lib_output_sphere( center, OUTPUT_FORMAT ) ;
/* check if children should be generated */
if ( depth > 0 ) {
--depth ;
/* rotation matrix to new axis from +Z axis */
if ( direction->z >= 1.0 ) {
/* identity matrix */
lib_create_identity_matrix( mx ) ;
}
else if ( direction->z <= -1.0 ) {
lib_create_rotate_matrix( mx, Y_AXIS, PI ) ;
}
else {
SET_COORD( z_axis, 0.0, 0.0, 1.0 ) ;
CROSS( axis, z_axis, (*direction) ) ;
lib_normalize_coord3( &axis ) ;
angle = acos( (double)DOT_PRODUCT( z_axis, (*direction) ) ) ;
lib_create_axis_rotate_matrix( mx, &axis, angle ) ;
}
/* scale down location of new spheres */
scale = center->w * (1.0 + direction->w ) ;
for ( num_vert = 0 ; num_vert < 9 ; ++num_vert ) {
lib_transform_coord( &child_pt, &objset[num_vert], mx ) ;
child_pt.x = child_pt.x * scale + center->x ;
child_pt.y = child_pt.y * scale + center->y ;
child_pt.z = child_pt.z * scale + center->z ;
/* scale down radius */
child_pt.w = center->w * direction->w ;
SUB3_COORD( child_dir, child_pt, (*center) ) ;
child_dir.x /= scale ;
child_dir.y /= scale ;
child_dir.z /= scale ;
child_dir.w = direction->w ;
output_object( depth, &child_pt, &child_dir ) ;
}
}
}
@//E*O*F balls.c//
chmod u=rw,g=r,o= balls.c
echo x - gears.c
sed 's/^@//' > "gears.c" <<'@//E*O*F gears.c//'
/*
* gears.c - Create a set of gears. Each gear face has 144 vertices, and
* contains concavities. Note that the first 3 vertices of all polygons
* define the two edges of a convex section of the polygon. Background
* square floor is reflective. Some gears are clipped.
* Five light sources.
*
* Version: 2.2 (11/17/87)
* Author: Eric Haines, 3D/Eye, Inc.
*
* SIZE_FACTOR determines the number of polygons output.
* Total gears = SF**3: concave polygons = 2 * SF**3
* rectangles = 4*TEETH * SF**3
*
* SIZE_FACTOR # gears # gear faces # rectangles
* 1 1 2 144
* 2 8 16 1152
* 3 27 54 3888
* 4 64 128 9216
*/
#include <stdio.h>
#include <math.h>
#include <memory.h>
#include "def.h"
#include "lib.h"
#define SIZE_FACTOR 4
/* define number of teeth on a gear - must be a multiple of 4 */
#define TEETH 36
/* define ratio of radius taken up by teeth and the gear thickness */
#define EDGE_RATIO 0.1
#define DEPTH_RATIO 0.1
main(argc,argv)
int argc ;
char *argv[] ;
{
COORD4 back_color, gear_color ;
COORD4 center_pt, floor[4], light, offset, zero_pt ;
COORD4 from, at, up ;
double angle, color_scale, outer_radius, thickness ;
long ix, iy, iz ;
/* output viewpoint */
SET_COORD( from, -1.1, -2.1, 2.6 ) ;
SET_COORD( at, 0.0, 0.0, 0.0 ) ;
SET_COORD( up, 0.0, 0.0, 1.0 ) ;
lib_output_viewpoint( &from, &at, &up, 45.0, 1.0, 512, 512 ) ;
/* output background color - UNC sky blue */
SET_COORD( back_color, 0.078, 0.361, 0.753 ) ;
lib_output_background_color( &back_color ) ;
/* output light sources */
SET_COORD( light, 2.0, 4.0, 4.0 ) ;
lib_output_light( &light ) ;
SET_COORD( light, -2.0, 4.0, 3.0 ) ;
lib_output_light( &light ) ;
SET_COORD( light, 2.0, -2.5, 2.5 ) ;
lib_output_light( &light ) ;
SET_COORD( light, -1.0, -4.0, 2.0 ) ;
lib_output_light( &light ) ;
/* just behind the eye */
SET_COORD( light, -1.111, -2.121, 2.626 ) ;
lib_output_light( &light ) ;
/* output floor polygon - off white */
SET_COORD( back_color, 1.0, 0.85, 0.7 ) ;
lib_output_color( &back_color, 0.75, 0.25, 25.0, 0.0, 0.0 ) ;
SET_COORD( floor[0], 2.0, 2.0, 0.0 ) ;
SET_COORD( floor[1], -2.0, 2.0, 0.0 ) ;
SET_COORD( floor[2], -2.0, -2.0, 0.0 ) ;
SET_COORD( floor[3], 2.0, -2.0, 0.0 ) ;
lib_output_polygon( 4, floor ) ;
outer_radius = 1.0 /
( (double)SIZE_FACTOR - (double)(SIZE_FACTOR-1) * EDGE_RATIO / 2.0 ) ;
/* calculate first gear center */
zero_pt.x = zero_pt.y = -1.0 + outer_radius ;
zero_pt.z = 1.0 ;
/* calculate offset */
offset.x = offset.y = outer_radius * ( 2.0 - EDGE_RATIO ) ;
offset.z = -1.0 / (double)SIZE_FACTOR ;
/* create gears */
for ( iz = 0 ; iz < SIZE_FACTOR ; ++iz ) {
center_pt.z = zero_pt.z + (double)iz * offset.z ;
for ( iy = 0 ; iy < SIZE_FACTOR ; ++iy ) {
center_pt.y = zero_pt.y + (double)iy * offset.y ;
for ( ix = 0 ; ix < SIZE_FACTOR ; ++ix ) {
center_pt.x = zero_pt.x + (double)ix * offset.x ;
/* output pseudo-random gear color */
SET_COORD( gear_color
, 0.01 + FRACTION( (double)(ix*3+iy*2+iz+1)*5.0/7.0 )
, 0.01 + FRACTION( (double)(iy*3+iz*2+ix+1)*3.0/7.0 )
, 0.01 + FRACTION( (double)(iz*3+ix*2+iy+1)*2.0/7.0 )
) ;
color_scale = MAX3( gear_color.x, gear_color.y, gear_color.z );
gear_color.x /= color_scale ;
gear_color.y /= color_scale ;
gear_color.z /= color_scale ;
if ( ( ix*4 + iy*2 + iz ) % 5 == 0 ) {
lib_output_color( &gear_color,
0.75, 0.25, 50.0, 0.95, 1.1 ) ;
}
else {
lib_output_color( &gear_color,
1.0, 0.0, 0.0, 0.0, 0.0 ) ;
}
/* output gear */
angle = PI * (double)( (ix+iy+iz) % 2 ) / (double)(TEETH) ;
thickness =
MIN( DEPTH_RATIO, 1.0 / ( 2.0 * (double)SIZE_FACTOR ) ) ;
create_gear( ¢er_pt,
angle,
outer_radius,
(1.0 - EDGE_RATIO) * outer_radius,
thickness ) ;
}
}
}
}
/* Create gear */
create_gear( center, offset_angle, outer_radius, inner_radius, thickness )
COORD4 *center ;
double offset_angle ;
double outer_radius ;
double inner_radius ;
double thickness ;
{
COORD4 side_pts[4], gear_pts[4*TEETH], outer_pt, inner_pt ;
long next_side, num_side, num_teeth ;
double gear_angle, tooth_angle ;
outer_pt.x = outer_radius ;
outer_pt.y = 0.0 ;
outer_pt.z = 0.0 ;
outer_pt.w = 1.0 ;
inner_pt.x = inner_radius ;
inner_pt.y = 0.0 ;
inner_pt.z = 0.0 ;
inner_pt.w = 1.0 ;
tooth_angle = 2.0 * PI / (double)TEETH ;
/* output gear top */
for ( num_teeth = 0 ; num_teeth < TEETH ; ++num_teeth ) {
gear_angle = offset_angle +
2.0 * PI * (double)num_teeth / (double)TEETH ;
create_tooth( gear_angle
, tooth_angle
, center
, &outer_pt
, &inner_pt
, &gear_pts[num_teeth*4]
) ;
}
lib_output_polygon( 4*TEETH, gear_pts ) ;
/* output teeth */
for ( num_side = 0 ; num_side < 4 * TEETH ; ++num_side ) {
next_side = ( num_side + 1 ) % ( 4 * TEETH ) ;
COPY_COORD( side_pts[0], gear_pts[num_side] ) ;
COPY_COORD( side_pts[1], gear_pts[num_side] ) ;
side_pts[1].z -= thickness ;
COPY_COORD( side_pts[2], gear_pts[next_side] ) ;
side_pts[2].z -= thickness ;
COPY_COORD( side_pts[3], gear_pts[next_side] ) ;
lib_output_polygon( 4, side_pts ) ;
}
/* output gear bottom */
outer_pt.z = inner_pt.z = -thickness ;
for ( num_teeth = 0 ; num_teeth < TEETH ; ++num_teeth ) {
gear_angle = offset_angle -
2.0 * PI * (double)num_teeth / (double)TEETH ;
create_tooth( gear_angle
, -tooth_angle
, center
, &outer_pt
, &inner_pt
, &gear_pts[num_teeth*4]
) ;
}
lib_output_polygon( 4*TEETH, gear_pts ) ;
}
/* Create gear tooth */
create_tooth( gear_angle, tooth_angle, center, outer_pt, inner_pt, edge_pts )
double gear_angle ;
double tooth_angle ;
COORD4 *center ;
COORD4 *outer_pt ;
COORD4 *inner_pt ;
COORD4 *edge_pts ;
{
MATRIX mx ;
lib_create_rotate_matrix( mx
, Z_AXIS
, gear_angle - 0.19 * tooth_angle ) ;
lib_transform_coord( &edge_pts[0], outer_pt, mx ) ;
ADD2_COORD( edge_pts[0], *center ) ;
lib_create_rotate_matrix( mx
, Z_AXIS
, gear_angle + 0.19 * tooth_angle ) ;
lib_transform_coord( &edge_pts[1], outer_pt, mx ) ;
ADD2_COORD( edge_pts[1], *center ) ;
lib_create_rotate_matrix( mx
, Z_AXIS
, gear_angle + 0.3 * tooth_angle ) ;
lib_transform_coord( &edge_pts[2], inner_pt, mx ) ;
ADD2_COORD( edge_pts[2], *center ) ;
lib_create_rotate_matrix( mx
, Z_AXIS
, gear_angle + 0.7 * tooth_angle ) ;
lib_transform_coord( &edge_pts[3], inner_pt, mx ) ;
ADD2_COORD( edge_pts[3], *center ) ;
}
@//E*O*F gears.c//
chmod u=rw,g=r,o= gears.c
echo x - mountain.c
sed 's/^@//' > "mountain.c" <<'@//E*O*F mountain.c//'
/*
* mountain.c - creates a fractal mountain, using Carpenter's method with a
* different extension to square grids. A pyramid of 4 glass spheres
* is added in front of the mountain. None of the spheres are clipped.
* A few of the polygons are clipped. One light source.
*
* Version: 2.2 (11/17/87)
* Author: Eric Haines, 3D/Eye, Inc.
*
* SIZE_FACTOR determines the number of objects output.
* Total triangular polygons = 2 * (4**SIZE_FACTOR)
*
* SIZE_FACTOR # triangles # spheres
* 1 8 4
* 2 32 4
* 3 128 4
*
* 6 8192 4
*/
#include <stdio.h>
#include <math.h>
#include <memory.h>
#include "def.h"
#include "lib.h"
#define OUTPUT_FORMAT OUTPUT_CURVES
/* size factor determines number of polygons */
#define SIZE_FACTOR 6
/* fractal dimension - affects variance of z. Between 2 and 3 */
#define FRACTAL_DIMENSION 2.2
/* change MOUNTAIN_NO to get a different mountain */
#define MOUNTAIN_NO 21
/* lower left corner and width of mountain definitions */
#define X_CORNER -1.0
#define Y_CORNER -1.0
#define WIDTH 2.0
/* hashing function to get a seed for the random number generator */
#define hash_rand(A,B,C) ( ( ((A)<<(23-(C))) + ((B)<<(15-(C)))\
+ ((A)<<(7-(C))) ) & 0xffff )
static long num_pts ;
static double roughness ;
main(argc,argv)
int argc ;
char *argv[] ;
{
COORD4 back_color, obj_color ;
COORD4 center, light ;
COORD4 from, at, up ;
double ratio ;
/* output viewpoint */
SET_COORD( from, -1.6, 1.6, 1.7 ) ;
SET_COORD( at, 0.0, 0.0, 0.0 ) ;
SET_COORD( up, 0.0, 0.0, 1.0 ) ;
lib_output_viewpoint( &from, &at, &up, 45.0, 0.5, 512, 512 ) ;
/* output background color - UNC sky blue */
SET_COORD( back_color, 0.078, 0.361, 0.753 ) ;
lib_output_background_color( &back_color ) ;
/* output light sources */
SET_COORD( light, -100.0, -100.0, 100.0 ) ;
lib_output_light( &light ) ;
/* set up crystal sphere color - clear white */
SET_COORD( obj_color, 1.0, 1.0, 1.0 ) ;
lib_output_color( &obj_color, 0.1, 0.9, 100.0, 0.9, 1.5 ) ;
/* output crystal spheres */
SET_COORD4( center, -0.8, 0.8, 1.00, 0.17 ) ;
create_spheres( ¢er ) ;
/* set up mountain color - grey */
SET_COORD( obj_color, 0.5, 0.45, 0.35 ) ;
lib_output_color( &obj_color, 1.0, 0.0, 0.0, 0.0, 0.0 ) ;
/* grow mountain */
num_pts = 1<<SIZE_FACTOR ;
ratio = 2.0 /
exp( (double)( log( (double)2.0 ) / (FRACTAL_DIMENSION-1.0) ) ) ;
roughness = sqrt( (double)( SQR(ratio) - 1.0 ) ) ;
grow_mountain( num_pts, 0, 0, 0.0, 0.0, 0.0, 0.0 ) ;
}
/* create a pyramid of crystal spheres */
create_spheres( center )
COORD4 *center ;
{
double angle ;
COORD4 axis, new_pt, pt, sphere ;
long i ;
MATRIX mx ;
SET_COORD( axis, 1.0, 1.0, 0.0 ) ;
lib_normalize_coord3( &axis ) ;
angle = acos( (double)( -1.0/3.0 ) ) ;
/* set center of pyramid and radius */
SET_COORD4( pt, 0.0, 0.0, center->w * sqrt( (double)( 3.0/2.0 ) ), 0.0 ) ;
COPY_COORD( sphere, pt ) ;
ADD2_COORD( sphere, *center ) ;
sphere.w = center->w ;
lib_output_sphere( &sphere, OUTPUT_FORMAT ) ;
lib_create_axis_rotate_matrix( mx, &axis, angle ) ;
lib_transform_coord( &new_pt, &pt, mx ) ;
for ( i = 0 ; i < 3 ; ++i ) {
lib_create_rotate_matrix( mx, Z_AXIS, (double)i * 2.0 * PI / 3.0 ) ;
lib_transform_coord( &sphere, &new_pt, mx ) ;
ADD2_COORD( sphere, *center ) ;
sphere.w = center->w ;
lib_output_sphere( &sphere, OUTPUT_FORMAT ) ;
}
}
/*
* Build mountain section. If at width > 1, split quadrilateral into four
* parts. Else if at width == 1, output quadrilateral as two triangles.
*/
grow_mountain( width, ll_x, ll_y, ll_fz, lr_fz, ur_fz, ul_fz )
long width ;
long ll_x ;
long ll_y ;
double ll_fz ;
double lr_fz ;
double ur_fz ;
double ul_fz ;
{
long half_width, iz ;
double l_fx, r_fx, l_fy, u_fy ;
double lower_fz, right_fz, upper_fz, left_fz, middle_fz ;
long num_tri, num_tri_vert, num_vert ;
double rise_height, hside_length ;
COORD4 tri_vert[3] ;
if ( width == 1 ) {
/* calculate x and y coordinates of corners */
l_fx = X_CORNER + (double)ll_x * WIDTH / (double)num_pts ;
r_fx = X_CORNER + (double)(ll_x+1) * WIDTH / (double)num_pts ;
l_fy = Y_CORNER + (double)ll_y * WIDTH / (double)num_pts ;
u_fy = Y_CORNER + (double)(ll_y+1) * WIDTH / (double)num_pts ;
/* output two triangles for section */
for ( num_tri = 0 ; num_tri < 2 ; ++num_tri ) {
for ( num_vert = 0 ; num_vert < 3 ; ++num_vert ) {
num_tri_vert = ( num_vert + num_tri * 2 ) % 4 ;
if ( num_tri_vert == 0 ) {
SET_COORD( tri_vert[num_vert], l_fx, l_fy, ll_fz ) ;
}
else if ( num_tri_vert == 1 ) {
SET_COORD( tri_vert[num_vert], r_fx, l_fy, lr_fz ) ;
}
else if ( num_tri_vert == 2 ) {
SET_COORD( tri_vert[num_vert], r_fx, u_fy, ur_fz ) ;
}
else {
SET_COORD( tri_vert[num_vert], l_fx, u_fy, ul_fz ) ;
}
}
lib_output_polygon( 3, tri_vert ) ;
}
}
else {
/* subdivide edges and move in z direction */
half_width = width>>1 ;
hside_length = (double)half_width * WIDTH / (double)num_pts ;
rise_height = hside_length * roughness ;
/* for each midpoint, find z */
iz = MOUNTAIN_NO + hash_rand( ll_x + half_width, ll_y, SIZE_FACTOR ) ;
lower_fz = ( ll_fz + lr_fz ) / 2.0 +
rise_height * lib_gauss_rand( iz ) ;
iz = MOUNTAIN_NO +
hash_rand( ll_x + width, ll_y + half_width, SIZE_FACTOR ) ;
right_fz = ( lr_fz + ur_fz ) / 2.0 +
rise_height * lib_gauss_rand( iz ) ;
iz = MOUNTAIN_NO +
hash_rand( ll_x + half_width, ll_y + width, SIZE_FACTOR ) ;
upper_fz = ( ur_fz + ul_fz ) / 2.0 +
rise_height * lib_gauss_rand( iz ) ;
iz = MOUNTAIN_NO + hash_rand( ll_x, ll_y + half_width, SIZE_FACTOR ) ;
left_fz = ( ul_fz + ll_fz ) / 2.0 +
rise_height * lib_gauss_rand( iz ) ;
iz = MOUNTAIN_NO +
hash_rand( ll_x + half_width, ll_y + half_width, SIZE_FACTOR ) ;
middle_fz = ( ll_fz + lr_fz + ur_fz + ul_fz ) / 4.0 +
1.4142136 * rise_height * lib_gauss_rand( iz ) ;
/* check subsections for subdivision or output */
grow_mountain( half_width, ll_x, ll_y,
ll_fz, lower_fz, middle_fz, left_fz ) ;
grow_mountain( half_width, ll_x+half_width, ll_y,
lower_fz, lr_fz, right_fz, middle_fz ) ;
grow_mountain( half_width, ll_x+half_width, ll_y+half_width,
middle_fz, right_fz, ur_fz, upper_fz ) ;
grow_mountain( half_width, ll_x, ll_y+half_width,
left_fz, middle_fz, upper_fz, ul_fz ) ;
}
}
@//E*O*F mountain.c//
chmod u=rw,g=r,o= mountain.c
echo x - rings.c
sed 's/^@//' > "rings.c" <<'@//E*O*F rings.c//'
/*
* rings.c - Create objects with 6 pentagonal rings which connect the midpoints
* of the edges of a dodecahedron. A pyramid of these objects is formed,
* which the viewer looks upon from the point. A plane is placed behind
* the pyramid for shadows. No object is clipped. Three light sources.
*
* Version: 2.2 (11/17/87)
* Author: Eric Haines, 3D/Eye, Inc.
*
* SIZE_FACTOR determines the number of objects output.
* Each object has 30 cylinders and 30 spheres.
* Total objects = SF*SF + (SF-1)*(SF-1) + ... + 1 plus 1 backdrop square.
* formula for # of spheres or cylinders = 5*SF*(SF+1)*(2*SF+1)
*
* SIZE_FACTOR # spheres # cylinders # squares
* 1 30 30 1
* 2 150 150 1
* 3 420 420 1
*
* 7 4200 4200 1
*/
#include <stdio.h>
#include <math.h>
#include <memory.h>
#include "def.h"
#include "lib.h"
#define OUTPUT_FORMAT OUTPUT_CURVES
#define SIZE_FACTOR 7
/* if spread out is > 1, succeeding layers spread out more */
#define SPREAD_OUT 1
main(argc,argv)
int argc ;
char *argv[] ;
{
COORD4 base_pt, apex_pt, offset ;
COORD4 wall[4], dodec[30] ;
COORD4 from, at, up ;
COORD4 wvec, light ;
COORD4 back_color, ring_color[6] ;
long prev_elem ;
long num_elem ;
long num_depth, num_objx, num_objz ;
double radius ;
double spread, y_diff, xz_diff ;
radius = 0.07412 ; /* cone and sphere radius */
/* calculate spread of objects */
spread = 1 / sin( (double)( PI/8.0 ) ) ;
if ( SPREAD_OUT <= spread ) {
y_diff = spread / SPREAD_OUT ;
xz_diff = 1.0 ;
}
else {
y_diff = 1.0 ;
xz_diff = SPREAD_OUT / spread ;
}
/* output viewpoint */
SET_COORD( from, -1.0, -spread, 0.5 ) ;
SET_COORD( at, from.x, from.y + 1.0, from.z ) ;
SET_COORD( up, 0.0, 0.0, 1.0 ) ;
lib_output_viewpoint( &from, &at, &up, 45.0, 1.0, 512, 512 ) ;
/* output background color - UNC sky blue */
/* note that the background color should never be seen */
SET_COORD( back_color, 0.078, 0.361, 0.753 ) ;
lib_output_background_color( &back_color ) ;
/* output light source */
SET_COORD( light, 3.0, -spread, 3.0 ) ;
lib_output_light( &light ) ;
SET_COORD( light, -4.0, -spread, 1.0 ) ;
lib_output_light( &light ) ;
SET_COORD( light, 2.0, -spread, -4.0 ) ;
lib_output_light( &light ) ;
/* output wall polygon - white */
SET_COORD( back_color, 1.0, 1.0, 1.0 ) ;
lib_output_color( &back_color, 1.0, 0.0, 0.0, 0.0, 0.0 ) ;
/* just spans 45 degree view + 1% */
wvec.y = y_diff * ( SIZE_FACTOR + 1 ) ;
wvec.x = wvec.z = 1.01 * ( wvec.y - from.y ) * tan( PI / 8.0 ) ;
SET_COORD( wall[0], wvec.x+from.x, wvec.y, wvec.z+from.z ) ;
SET_COORD( wall[1], -wvec.x+from.x, wvec.y, wvec.z+from.z ) ;
SET_COORD( wall[2], -wvec.x+from.x, wvec.y, -wvec.z+from.z ) ;
SET_COORD( wall[3], wvec.x+from.x, wvec.y, -wvec.z+from.z ) ;
lib_output_polygon( 4, wall ) ;
/* set up ring colors - RGB and complements */
SET_COORD( ring_color[0], 1.0, 0.0, 0.0 ) ;
SET_COORD( ring_color[1], 0.0, 1.0, 0.0 ) ;
SET_COORD( ring_color[2], 0.0, 0.0, 1.0 ) ;
SET_COORD( ring_color[3], 0.0, 1.0, 1.0 ) ;
SET_COORD( ring_color[4], 1.0, 0.0, 1.0 ) ;
SET_COORD( ring_color[5], 1.0, 1.0, 0.0 ) ;
create_dodec( radius, dodec ) ;
/* radius of osculating cylinders and spheres (no derivation given) */
base_pt.w = apex_pt.w = radius ;
for ( num_depth = 0 ; num_depth < SIZE_FACTOR ; ++num_depth ) {
offset.y = y_diff * (double)(num_depth+1) ;
for ( num_objz = 0 ; num_objz <= num_depth ; ++num_objz ) {
offset.z = xz_diff * (double)(2*num_objz - num_depth) ;
for ( num_objx = 0 ; num_objx <= num_depth ; ++num_objx ) {
offset.x = xz_diff * (double)(2*num_objx - num_depth) ;
for ( num_elem = 0 ; num_elem < 30 ; ++num_elem ) {
COPY_COORD( base_pt, dodec[num_elem] ) ;
ADD2_COORD( base_pt, offset ) ;
if ( num_elem%5 == 0 ) {
prev_elem = num_elem + 4 ;
/* new ring beginning - output color */
lib_output_color( &ring_color[num_elem/5]
, 0.5
, 0.5, 3.0
, 0.0, 0.0
) ;
}
else {
prev_elem = num_elem - 1 ;
}
COPY_COORD( apex_pt, dodec[prev_elem] ) ;
ADD2_COORD( apex_pt, offset ) ;
lib_output_cylcone( &base_pt, &apex_pt, OUTPUT_FORMAT ) ;
lib_output_sphere( &base_pt, OUTPUT_FORMAT ) ;
}
}
}
}
}
/* Create the set of 30 points needed to generate the rings */
create_dodec( minor_radius, vertex )
double minor_radius ;
COORD4 vertex[30] ;
{
long num_vertex, num_pentagon ;
COORD4 temp_vertex ;
MATRIX x_matrix, z_matrix ;
double scale, x_rotation, z_rotation ;
/* scale object to fit in a sphere of radius 1 */
scale = 1.0 / ( 1.0 + minor_radius ) ;
/*
* define one pentagon as on the XY plane, with points starting along +X
* and N fifths of the way around the Z axis.
*/
for ( num_vertex = 0 ; num_vertex < 5 ; ++num_vertex ) {
vertex[num_vertex].x = scale * cos( (double)num_vertex * 2.0*PI/5.0 ) ;
vertex[num_vertex].y = scale * sin( (double)num_vertex * 2.0*PI/5.0 ) ;
vertex[num_vertex].z = 0.0 ;
vertex[num_vertex].w = 1.0 ;
}
/*
* find the rotation angle (in radians) along the X axis:
* angle between two adjacent dodecahedron faces.
*/
x_rotation = 2.0 *
acos( cos( (double)(PI/3.0) ) / sin( (double)(PI/5.0) ) ) ;
lib_create_rotate_matrix( x_matrix, X_AXIS, x_rotation ) ;
/*
* Find each of the other 5 pentagons: rotate along the X axis,
* then rotate on the Z axis.
*/
for ( num_pentagon = 1 ; num_pentagon < 6 ; ++num_pentagon ) {
/*
* find the rotation angle (in radians) along the Z axis:
* 1/10th plus N fifths of the way around * 2 * PI.
*/
z_rotation = PI*( 2.0*(double)(num_pentagon-1)+1.0 ) / 5.0 ;
lib_create_rotate_matrix( z_matrix, Z_AXIS, z_rotation ) ;
for ( num_vertex = 0 ; num_vertex < 5 ; ++num_vertex ) {
lib_transform_coord( &temp_vertex
, &vertex[num_vertex]
, x_matrix
) ;
lib_transform_coord( &vertex[5*num_pentagon+num_vertex]
, &temp_vertex
, z_matrix
) ;
}
}
}
@//E*O*F rings.c//
chmod u=rw,g=r,o= rings.c
echo x - tetra.c
sed 's/^@//' > "tetra.c" <<'@//E*O*F tetra.c//'
/*
* tetra.c - Create a tetrahedral pyramid. This environment is based on the
* scene used by Glassner ("Space Subdivision for Fast Ray Tracing," IEEE
* CG&A, October 1984) and Kay & Kajiya ("Ray Tracing Complex Scenes,"
* SIGGRAPH '86 Proceedings) for testing their ray tracers. No polygons
* are clipped. One light source.
*
* Version: 2.2 (11/17/87)
* Author: Eric Haines, 3D/Eye, Inc.
*
* Note: the view and light positions are the same (after transformation to
* a different set of world coordinates) as used by Kay & Kajiya,
* courtesy of Tim Kay. For some reason, the number of shadow rays
* generated is different (Kay gets 34K, I get 46K). One light source.
*
* SIZE_FACTOR determines the number of polygons output.
* Total triangular polygons = 4**SF
*
* SIZE_FACTOR # triangles
* 1 4
* 2 16
* 3 64
*
* 6 4096
*/
#include <stdio.h>
#include <math.h>
#include <memory.h>
#include "def.h"
#include "lib.h"
#define SIZE_FACTOR 6
main(argc,argv)
int argc ;
char *argv[] ;
{
COORD4 back_color, tetra_color ;
COORD4 center_pt, light ;
COORD4 from, at, up ;
/* output viewpoint */
SET_COORD( from, 1.022846, -3.177154, -2.174512 ) ;
SET_COORD( at, -0.004103, -0.004103, 0.216539 ) ;
SET_COORD( up, -0.816497, -0.816497, 0.816497 ) ;
lib_output_viewpoint( &from, &at, &up, 45.0, 1.0, 512, 512 ) ;
/* output background color - UNC sky blue */
SET_COORD( back_color, 0.078, 0.361, 0.753 ) ;
lib_output_background_color( &back_color ) ;
/* output light source */
SET_COORD( light, 1.876066, -18.123936, -5.000422 ) ;
lib_output_light( &light ) ;
/* output tetrahedron color - red */
SET_COORD( tetra_color, 1.0, 0.2, 0.2 ) ;
lib_output_color( &tetra_color, 1.0, 0.0, 0.0, 0.0, 0.0 ) ;
/* compute and output tetrahedral object */
SET_COORD4( center_pt, 0.0, 0.0, 0.0, 1.0 ) ;
create_tetra( SIZE_FACTOR, ¢er_pt ) ;
}
/* Create tetrahedrons recursively */
create_tetra( depth, center )
long depth ;
COORD4 *center ;
{
long num_face, num_vert ;
COORD4 face_pt[3], obj_pt[4], sub_center ;
long swap, vert_ord[3] ;
long x_dir, y_dir, z_dir ;
if ( depth <= 1 ) {
/* Output tetrahedron */
/* find opposite corners of a cube which form a tetrahedron */
for ( num_vert = 0, x_dir = -1 ; x_dir <= 1 ; x_dir += 2 ) {
for ( y_dir = -1 ; y_dir <= 1 ; y_dir += 2 ) {
for ( z_dir = -1 ; z_dir <= 1 ; z_dir += 2 ) {
if ( x_dir*y_dir*z_dir == 1 ) {
obj_pt[num_vert].x =
center->x + (double)x_dir * center->w ;
obj_pt[num_vert].y =
center->y + (double)y_dir * center->w ;
obj_pt[num_vert].z =
center->z + (double)z_dir * center->w ;
++num_vert ;
}
}
}
}
/* find faces and output */
for ( num_face = 0 ; num_face < 4 ; ++num_face ) {
/* output order:
* face 0: points 0 1 2
* face 1: points 3 2 1
* face 2: points 2 3 0
* face 3: points 1 0 3
*/
for ( num_vert = 0 ; num_vert < 3 ; ++num_vert ) {
vert_ord[num_vert] = (num_face + num_vert) % 4 ;
}
if ( num_face%2 == 1 ) {
swap = vert_ord[0] ;
vert_ord[0] = vert_ord[2] ;
vert_ord[2] = swap ;
}
for ( num_vert = 0 ; num_vert < 3 ; ++num_vert ) {
COPY_COORD( face_pt[num_vert], obj_pt[vert_ord[num_vert]] ) ;
}
lib_output_polygon( 3, face_pt ) ;
}
}
else {
/* Create sub-tetrahedra */
/* find opposite corners of a cube to form sub-tetrahedra */
for ( x_dir = -1 ; x_dir <= 1 ; x_dir += 2 ) {
for ( y_dir = -1 ; y_dir <= 1 ; y_dir += 2 ) {
for ( z_dir = -1 ; z_dir <= 1 ; z_dir += 2 ) {
if ( x_dir*y_dir*z_dir == 1 ) {
sub_center.x =
center->x + (double)x_dir * center->w / 2.0 ;
sub_center.y =
center->y + (double)y_dir * center->w / 2.0 ;
sub_center.z =
center->z + (double)z_dir * center->w / 2.0 ;
sub_center.w = center->w / 2.0 ;
create_tetra( depth-1, &sub_center ) ;
}
}
}
}
}
}
@//E*O*F tetra.c//
chmod u=rw,g=r,o= tetra.c
echo x - tree.c
sed 's/^@//' > "tree.c" <<'@//E*O*F tree.c//'
/*
* tree.c - Creates a tree using Aono & Kunii's generation method.
* (See IEEE CG&A May 1984). A square polygon is placed beneath the
* tree to act as a field. No tree branch is clipped. Seven light sources.
*
* Version: 2.2 (11/17/87)
* Author: Eric Haines, 3D/Eye, Inc.
*
* SIZE_FACTOR determines the number of objects output.
* Total objects = 2**(SF+1)-1 cones and spheres + 1 square polygon.
*
* SIZE_FACTOR # spheres # cones # squares
* 1 3 3 1
* 2 7 7 1
* 3 15 15 1
*
* 11 4095 4095 1
*/
#include <stdio.h>
#include <math.h>
#include <memory.h>
#include "def.h"
#include "lib.h"
#define OUTPUT_FORMAT OUTPUT_CURVES
#define SIZE_FACTOR 11
/* the following affect the shape of the tree */
#define BR_ANGLE_0 40.0
#define BR_ANGLE_1 25.0
#define BR_CONTR_0 0.65
#define BR_CONTR_1 0.70
#define BR_DIAMETER 0.67
#define DIV_ANGLE 140.0
#define WIDTH_HEIGHTH_RATIO 0.15
static MATRIX rst_mx[2] ;
main(argc,argv)
int argc ;
char *argv[] ;
{
COORD4 field[4] ;
COORD4 from, at, up ;
COORD4 light ;
COORD4 back_color, tree_color ;
/* output viewpoint */
SET_COORD( from, 4.5, 0.4, 2.0 ) ;
SET_COORD( at, 0.0, 0.0, 1.5 ) ;
SET_COORD( up, 0.0, 0.0, 1.0 ) ;
lib_output_viewpoint( &from, &at, &up, 45.0, 1.0, 512, 512 ) ;
/* output background color - UNC sky blue */
SET_COORD( back_color, 0.078, 0.361, 0.753 ) ;
lib_output_background_color( &back_color ) ;
/* output light source */
SET_COORD( light, -5.0, 5.0, 50.0 ) ;
lib_output_light( &light ) ;
SET_COORD( light, 30.0, -30.0, 30.0 ) ;
lib_output_light( &light ) ;
SET_COORD( light, -40.0, -30.0, 20.0 ) ;
lib_output_light( &light ) ;
SET_COORD( light, 10.0, 30.0, 40.0 ) ;
lib_output_light( &light ) ;
SET_COORD( light, -30.0, 40.0, 10.0 ) ;
lib_output_light( &light ) ;
SET_COORD( light, 50.0, 25.0, 20.0 ) ;
lib_output_light( &light ) ;
SET_COORD( light, -10.0, -60.0, 30.0 ) ;
lib_output_light( &light ) ;
/* output field polygon - green */
SET_COORD( back_color, 0.2, 0.7, 0.2 ) ;
lib_output_color( &back_color, 1.0, 0.0, 0.0, 0.0, 0.0 ) ;
SET_COORD( field[0], 50.0, 50.0, 0.0 ) ;
SET_COORD( field[1], -50.0, 50.0, 0.0 ) ;
SET_COORD( field[2], -50.0, -50.0, 0.0 ) ;
SET_COORD( field[3], 50.0, -50.0, 0.0 ) ;
lib_output_polygon( 4, field ) ;
/* set up tree color - brown */
SET_COORD( tree_color, 0.55, 0.4, 0.2 ) ;
lib_output_color( &tree_color, 1.0, 0.0, 0.0, 0.0, 0.0 ) ;
/* create tree */
create_tree() ;
}
/*
* Set up matrices for growth of each branch with respect to the
* parent branch, then grow each branch.
*/
create_tree()
{
double branch_angle, branch_contraction, divergence ;
long i ;
MATRIX ident_mx, temp1_mx, temp2_mx, tempr_mx, tempst_mx ;
for ( i = 0 ; i < 2 ; ++i ) {
if ( i == 0 ) {
branch_angle = BR_ANGLE_0 ;
divergence = 90.0 ;
branch_contraction = BR_CONTR_0 ;
}
else {
branch_angle = BR_ANGLE_1 ;
divergence = DIV_ANGLE + 90.0 ;
branch_contraction = BR_CONTR_1 ;
}
/* rotate along X axis by branching angle */
lib_create_rotate_matrix( temp1_mx, X_AXIS, branch_angle*PI/180.0 ) ;
/* rotate along Z axis by divergence angle */
lib_create_rotate_matrix( temp2_mx, Z_AXIS, divergence*PI/180.0 ) ;
lib_matrix_multiply( tempr_mx, temp1_mx, temp2_mx ) ;
/* include translation of branch, scaled */
lib_create_identity_matrix( tempst_mx ) ;
tempst_mx[0][0] = tempst_mx[1][1] = tempst_mx[2][2] =
branch_contraction ;
tempst_mx[3][2] = 1.0 ;
/* concatenate */
lib_matrix_multiply( rst_mx[i], tempr_mx, tempst_mx ) ;
}
/* set up initial matrix */
lib_create_identity_matrix( ident_mx ) ;
grow_tree( ident_mx, 1.0, SIZE_FACTOR ) ;
}
/* grow tree branches recursively */
grow_tree( cur_mx, scale, depth )
MATRIX cur_mx ;
double scale ;
long depth ;
{
COORD4 apex, base, vec ;
long i ;
MATRIX new_mx ;
/* output branch */
SET_COORD4( vec, 0.0, 0.0, 0.0, 1.0 ) ;
lib_transform_coord( &base, &vec, cur_mx ) ;
base.w = scale * WIDTH_HEIGHTH_RATIO ;
SET_COORD4( vec, 0.0, 0.0, 1.0, 1.0 ) ;
lib_transform_coord( &apex, &vec, cur_mx ) ;
apex.w = base.w * BR_DIAMETER ;
lib_output_cylcone( &base, &apex, OUTPUT_FORMAT ) ;
lib_output_sphere( &apex, OUTPUT_FORMAT ) ;
if ( depth > 0 ) {
--depth ;
for ( i = 0; i < 2; ++i ) {
lib_matrix_multiply( new_mx, rst_mx[i], cur_mx ) ;
grow_tree( new_mx, scale * BR_DIAMETER, depth ) ;
}
}
}
@//E*O*F tree.c//
chmod u=rw,g=r,o= tree.c
exit 0
From saponara@tcgould.tn.cornell.edu Tue Sep 6 10:01:53 1988
Path: seismo!uunet!husc6!mailrus!cornell!batcomputer!saponara
From: saponara@batcomputer.tn.cornell.edu (John Saponara)
Newsgroups: comp.graphics
Subject: Re: Haine's NFF Package: Part 01/01
Message-ID: <6220@batcomputer.tn.cornell.edu>
Date: 6 Sep 88 14:01:53 GMT
References: <2684@uoregon.uoregon.edu>
Reply-To: saponara@tcgould.tn.cornell.edu (Eric Haines, actually)
Organization: 3D/Eye Inc (not Cornell, actually)
Lines: 249
In article <2684@uoregon.uoregon.edu> markv@drizzle.UUCP (Mark VandeWettering) writes:
>
>
>This is as it was posted to comp.graphics AGES ago. Have fun. If
>someone figures out why "gears" turns out funny on my raytracer, let me
>know, I am stumped.
One possibility is that you've got an old version of gears.c. A bug that
K.R. Subramanian pointed out to me was that the gear polygons overlap, which
causes refraction to be pretty ill-defined.
Attached are the patches to bring your posted version of the SPD package up
to version 2.4. Note that the one available on netlib is at 2.4, so does
not need these patches.
Eric Haines (not John Saponara - do you believe me or the computer?)
*** old/README
--- new/README
4c4
< Version 2.2, as of 11/17/87
---
> Version 2.4, as of 5/1/88
17a18,21
> Version 2.3 released March, 1988 - corrected gears.c to avoid interpenetration,
> corrected and added further instructions and global statistics for ray
> tracing to README.
> Version 2.4 released May, 1988 - fixed hashing function for mountain.c.
55a60,63
> The images for these databases and other information about them can be
> found in "A Proposal for Standard Graphics Environments," IEEE Computer
> Graphics & Applications, November 1987, pages 3-5. A corrected image of the
> tree appears in IEEE CG&A, January 1987, page 18.
56a65,69
> At present, the SPD package is available for the IBM PC on 360K 5.25"
> floppy for $5 from: MicroDoc, c/o F.W. Pospeschil, 3108 Jackson Street,
> Bellevue, Nebraska 68005.
>
>
138c151
< background 0% 7% 47% 0% 81% 35%
---
> background 0% 7% 34% 0% 81% 35%
141,142d153
< ave tree size 1.79 3.02 3.71 2.19 1.00 1.00
< ave light rays 3.39 6.43 0.80 4.09 0.18 4.17
143a155,159
> eye hit rays 263169 245532 173422 263169 49806 170134
> reflect rays 175616 305561 355355 316621 0 0
> refract rays 0 208153 355355 0 0 0
> shadow rays 954544 2126105 362657 1087366 46150 1099748
>
171,176c187,191
< percentage of background color (empty space) seen directly by the eye. Note
< that any information in units of pixels is view dependent, and so is for the
< view specified. "ave tree size" is the average number of rays in a ray tree.
< "ave light rays" is the average number of shadow testing rays formed per tree
< (note that if a surface is facing away from a light, or the background is hit,
< a light ray is not formed). "K" means exactly 1000 (not 1024).
---
> percentage of background color (empty space) seen directly by the eye for the
> given view. It is calculated by "1 - ( eye hit rays/(513*513) )", since
> 513 x 513 rays are generated from the eye. "specular" tells if there are
> reflective objects in the scene, and "transmitter" if there are transparent
> objects.
177a193,200
> "eye hit rays" is the number of rays from the eye which actually hit an
> object (i.e. not the background). "reflect rays" is the total number of rays
> generated by reflection off of reflecting and transmitting surfaces. "refract
> rays" is the number of rays generated by transmitting surfaces. "shadow rays"
> is the sum total of rays shot towards the lights. Note that if a surface is
> facing away from a light, or the background is hit, a light ray is not formed.
> The numbers given can vary noticeably from a given ray tracer, but should all
> be within about 10%.
178a202,204
> "K" means exactly 1000 (not 1024), with number rounded to the nearest K.
>
>
341,344c367,369
< corners (meaning that at least 513 x 513 eye rays will be created).
< The four corner contributions are averaged to arrive at a pixel
< value. If this is not done, note this fact. No pixel subdivision
< is performed.
---
> corners (meaning that 513 x 513 eye rays will be created). The four
> corner contributions are averaged to arrive at a pixel value. If this
> is not done, note this fact. No pixel subdivision is performed.
348,352c373,377
< 5) All rays hitting specular and transmitting objects spawn reflection
< rays, unless the maximum ray depth was reached by the spawning ray.
< No adaptive tree depth cutoff is allowed; that is, all rays must be
< spawned (adaptive tree depth is a proven time saver and is also
< dependent on the color model used).
---
> 5) All rays hitting only specular and transmitting objects spawn
> reflection rays, unless the maximum ray depth was reached by the
> spawning ray. No adaptive tree depth cutoff is allowed; that is, all
> rays must be spawned (adaptive tree depth is a proven time saver and
> is also dependent on the color model used).
355,358c380,383
< the maximum ray depth was reached. Transmitting rays should be
< refracted (i.e. should not pass straight through an object). If total
< internal reflection of a ray occurs, then only a reflection ray is
< generated at this node.
---
> the maximum ray depth was reached or total internal reflection occurs.
> Transmitting rays should be refracted using Snell's (i.e. should not
> pass straight through an object). If total internal reflection occurs,
> then only a reflection ray is generated at this node.
360,361c385,386
< 7) Assume no hierarchy is given with the database (for example, color
< change cannot be used to signal a clustering).
---
> 7) A shadow ray is not generated if the surface normal points away from
> the light.
363c388,392
< 8) Timing costs should be separated into at least two areas: preprocessing
---
> 8) Assume no hierarchy is given with the database (for example, color
> change cannot be used to signal a clustering). The ray tracing program
> itself can create its own hierarchy, of course.
>
> 9) Timing costs should be separated into at least two areas: preprocessing
371c400
< 9) Other timing costs which would be of interest is a breakdown of
---
> 10) Other timing costs which would be of interest is a breakdown of
389c418
< Kflops. However, some HP hardware engineer told me that the HP 320 is twice
---
> Kflops. However, an HP hardware engineer told me that the HP 320 is twice
396c425
< language used (i.e. optimized "C", single point precision in general).
---
> language used (i.e. optimized "C", single precision in general).
466c495
< number of eye rays which hit background: 213219 [ 81% ]
---
> number of eye rays which hit background: 213363 [ 81% ]
469c498
< total number of shadow rays generated: 46262
---
> total number of shadow rays generated: 46150
490,491c519,520
< environment). Same question with the effects of moving light source positions
< along the line defined by the light and "lookat" positions.
---
> environment). Same question with the effects on the algorithms of moving light
> source positions along the line defined by the light and "lookat" positions.
520,526c549,555
< Elmquist, Jeff Goldsmith, Donald Greenberg, Susan Spach, Rick Speer, John
< Wallace, and Louise Watson. Other people who have freely offered their ideas
< and opinions on this project include Brian Barsky, Andrew Glassner, Roy Hall,
< Chip Hatfield, Tim Kay, John Recker, Paul Strauss, and Chan Verbeck. These
< names are mentioned mostly as a listing of people interested in this idea.
< They do not necessarily agree (and in some cases strongly disagree) with the
< validity of the concept or the choice of databases.
---
> Elmquist, Jeff Goldsmith, Donald Greenberg, Susan Spach, Rick Speer, K.R.
> Subramanian, John Wallace, and Louise Watson. Other people who have freely
> offered their ideas and opinions on this project include Brian Barsky, Andrew
> Glassner, Roy Hall, Chip Hatfield, Tim Kay, John Recker, Paul Strauss, and Chan
> Verbeck. These names are mentioned mostly as a listing of people interested in
> this idea. They do not necessarily agree (and in some cases strongly disagree)
> with the validity of the concept or the choice of databases.
*** old/gears.c
--- new/gears.c
6c6
< * Five light sources.
---
> * Five light sources.
8c8
< * Version: 2.2 (11/17/87)
---
> * Version: 2.3 (3/1?/88)
33c33,41
< #define EDGE_RATIO 0.1
---
> /* the outer radius is made slightly smaller that the full radius to create
> * a finite separation between intermeshing gears. This gets rid of the bug
> * of having two surfaces occupy exactly the same space. Note that if these
> * are changed, the gears may interpenetrate.
> */
> #define OUTER_EDGE_RATIO 0.995
> #define INNER_EDGE_RATIO 0.9
> #define EDGE_DIFF ( 1.0 - INNER_EDGE_RATIO )
> /* ratio of width of gear to thickness */
81c89
< ( (double)SIZE_FACTOR - (double)(SIZE_FACTOR-1) * EDGE_RATIO / 2.0 ) ;
---
> ( (double)SIZE_FACTOR - (double)(SIZE_FACTOR-1) * EDGE_DIFF / 2.0 ) ;
86c94
< offset.x = offset.y = outer_radius * ( 2.0 - EDGE_RATIO ) ;
---
> offset.x = offset.y = outer_radius * ( 2.0 - EDGE_DIFF ) ;
122,123c130,131
< outer_radius,
< (1.0 - EDGE_RATIO) * outer_radius,
---
> OUTER_EDGE_RATIO * outer_radius,
> (1.0 - EDGE_DIFF) * outer_radius,
*** old/mountain.c
--- new/mountain.c
7c7,15
< * Version: 2.2 (11/17/87)
---
> * NOTE: the hashing function used to generate the database originally is
> * faulty. The function causes repetition to occur within the fractal
> * mountain (obviously not very fractal behavior!). A new hashing function
> * is included immediately after the old one: merely define NEW_HASH if
> * you want to use a good hashing function. To perform ray tracing
> * comparison tests you should still use the old, faulty database (it may
> * have repetition, but it's still a good test image).
> *
> * Version: 2.4 (5/1/88)
26a35,37
> /* to use the corrected hashing function, uncomment this next line */
> /* #define NEW_HASH */
>
41c52,56
< /* hashing function to get a seed for the random number generator */
---
> #ifndef NEW_HASH
>
> /* Hashing function to get a seed for the random number generator. */
> /* This is the old, buggy hashing function - use it if you wish to
> * obtain the same image as in the November 1987 IEEE CG&A article. */
44a60,77
> #else
>
> /* New, corrected hashing function. Use for a true fractal mountain */
> /* 134456 is M1 in routine lib_gauss_rand() */
> #define hash_rand(A,B,C) ( ( C <= 15 ) ? \
> ( ABSOLUTE( \
> ((A)<<(31-(C))) \
> + ((B)<<(15-(C))) ) \
> % 134456 ) \
> : \
> ( ABSOLUTE( \
> ((A)<<(31-(C))) \
> + ((B)>>((C)-15)) ) \
> % 134456 ) \
> )
>
> #endif
>
56d88
<